理解springboot自动配置原理
springboot 的诞生就是为了简化 spring 中繁琐的 xml 配置,其本质依然还是spring框架,使用springboot之后可以不使用任何 xml 配置来启动一个服务,使得我们在使用微服务架构时可以更加快速的建立一个应用。
简单来说就是springboot其实不是什么新的框架,它默认配置了很多框架的使用方式。
下面探究springboot的启动原理,关于一些细节就不赘述,我们捉住主线分析即可。
注意:本文的 springboot 版本为 2.6.1
一切的来自起源springboot的启动类,我们发现main方法上面有个注解:@springbootapplication
@springbootapplicationpublic class springbootworkapplicatio心急如焚的近义词和反义词n { public static void main(string[] args) { springapplication.run(springbootworkapplication.class, args); }}
@springbootapplication 标注在某个类上说明这个类是 springboot 的主配置类, springboot 就应该运行这个类的main方法来启动 springboot 应用;它的本质是一个组合注解,我们点进去查看该类的元信息主要包含3个注解:
@target({139youxiangelementtype.type})@retention(retentionpolicy.runtime)@documented@inherited@springbootconfiguration@enableautoconfiguration@componentscan( excludefilters = {@filter( type = filtertype.custom, class = {typeexcludefilter.class}), @filter( type = filtertype.custom, class = {autoconfigurationexcludefilter.class})})public @interface springbootapplication {
@springbootconfiguration
(里面就是@configuration,标注当前类为配置类,其实只是做了一层封装改了个名字而已)@enableautoconfiguration
(开启自动配置)@componentscan
(包扫描)注:@inherited是一个标识,用来修饰注解,如果一个类用上了@inh幼儿诗歌朗诵erited修饰的注解,那么其子类也会继承这个注解
我们下面逐一分析这3个注解作用
3.1.1 @springbootconfiguration
我们继续点@springbootconfiguration进去查看源码如下:
@target({elementtype.type})@retention(retentionpolicy.runtime)@documented@configuration@indexedpublic @interface springbootconfiguration { @aliasfor( annotation = configuration.class ) boolean proxybeanmethods() default true;}
@configuration标注在某个类上,表示这是一个 springboot的配置类。可以向容器中注入组件。
3.1.2 @componentscan
@componentscan:配置用于 configuration 类的组件扫描指令。提供与 spring xml 的 <context:component-scan> 元素并行的支持。可以 bapackageclass 或bapackages 来定义要扫描的特定包。 如果没有定义特定的包,将从声明该注解的类的包开始扫描。3.1.3 @enableautoconfiguration
@enableautoconfiguration顾名思义就是:开启自动导入配置这个注解是springboot的重点,我们下面详细讲解我们点进去看看该注解有什么内容
@target({elementtype.type})@retention(retentionpolicy.runtime)@documented@inherited@autoconfigurationpackage //自动导包@import({autoconfigurationimportlector.class}) //自动配置导入选择public @interface enableautoconfiguration { string enabled_override_property = "spring.boot.enableautoconfiguration"; class<?>[] exclude() default {}; string[] excludename() default {};}
自动导入配置包
点进去查看代码:
@target({elementtype.type})@retention(retentionpolicy.runtime)@documented@inherited@import({registrar.class})public @interface autoconfigurationpackage { string[] bapackages() default {}; class<?>[] bapackageclass() default {};}
@import 为spring的注解,导入一个配置文件,在springboot中为给容器导入一个组件,而导入的组件由 autoconfigurationpackages.class的内部类registrar.class 执行逻辑来决定是如何导入的。
4.1.1 @import({registrar.class})
点registrar.class进去查看源码如下:
static class registrar implements importbeandefinitionregistrar, determinableimports { registrar() { } public void registerbeandefinitions(annotationmetadata metadata, beandefinitionregistry registry) { //断点 autoconfigurationpackages.register(registry, (string[])(new autoconfigurationpackages.packageimports(metadata)).getpackagenames().toarray(new string[0])); } public t<object> determineimports(annotationmetadata metadata) { return collections.singleton(new autoconfigurationpackages.packageimports(metadata)); }}
注:registrar实现了importbeandefinitionregistrar类,就可以被注解@import导入到spring容器里。
这个地方打断点
运行可以查看到(string[])(new autoconfigurationpackages.packageimports(metadata)).getpackagenames().toarray(new string[0])的值为com.ljw.springbootwork:当前启动类所在的包名
结论:@autoconfigurationpackage 就是将主配置类(@springbootapplication 标注的类)所在的包下面所有的组件都扫描注冊到 spring 容器中。
作用:autoconfigurationimportlector开启自动配置类的导包的选择器,即是带入哪些类,有选择性的导入
点autoconfigurationimportlector.class进入查看源码,这个类中有两个方法见名知意:
1.lectimports:选择需要导入的组件
public string[] lectimports(annotationmetadata annotationmetadata) { if (!this.inabled(annotationmetadata)) { return no_imports; } el { autoconfigurationimportlector.autoconfigurationentry autoconfigurationentry = this.getautoconfigurationentry(annotationmetadata); return stringutils.tostringarray(autoconfigurationentry.getconfigurations()); }}
2.getautoconfigurationentry:根据导入的@configuration类的annotationmetadata返回autoconfigurationimportlector.autoconfigurationentry
protected autoconfigurationimportlector.autoconfigurationentry getautoconfigurationentry(annotationmetadata annotationmetadata) { if (!this.inabled(annotationmetadata)) { return empty_entry; } el { annotationattributes attributes = this.getattributes(annotationmetadata); // 这打个断点,看看 返回的数据 list<string> configurations = this.getcandidateconfigurations(annotationmetadata, attributes); //删除重复项 configurations = this.removeduplicates(configurations); t<string> exclusions = this.getexclusions(annotationmetadata, attributes); //检查 this.checkexcludedclass(configurations, exclusions); //删除需要排除的依赖 configurations.removeall(exclusions); configurations = this.getconfigurationclassfilter().filter(configurations); this.fireautoconfigurationimportevents(configurations, exclusions); return new autoconfigurationimportlector.autoconfigurationentry(configurations, exclusions); }}
this.getcandidateconfigurations(annotationmetadata, attributes)这里断点查看
configurations数组长度为133,并且文件后缀名都为 **autoconfiguration
结论: 这些都是候选的配置类,经过去重,去除需要的排除的依赖,最终的组件才是这个环境需要的所有组件。有了自动配置,就不需要我们自己手写配置的值了,配置类有默认值的。
我们继续往下看看是如何返回需要配置的组件的
4.2.1 getcandidateconfigurations(annotationmetadata, attributes)
方法如下:
protected list<string> getcandidateconfigurations(annotationmetadata metadata, annotationattributes attributes) { list<string> configurations = springfactoriesloader.loadfactorynames(this.getspringfactoriesloaderfactoryclass(), this.getbeanclassloader()); asrt.notempty(configurations, "no auto configuration class found in meta-inf/spring.factories. if you are using a custom packaging, make sure that file is correct."); return configurations;}
这里有句断言: asrt.notempty(configurations, “no auto configuration class found in meta-inf/spring.factories. if you are using a custom packaging, make sure that file is correct.”);
意思是:“在 meta-inf/spring.factories 中没有找到自动配置类。如果您使用自定义包装,请确保该文件是正确的。“
结论: 即是要loadfactorynames()方法要找到自动的配置类返回才不会报错。
4.2.1.1 getspringfactoriesloaderfactoryclass()
我们点进去发现:this.getspringfactoriesloaderfactoryclass()返回的是enableautoconfiguration.class这个注解。这个注解和@springbootapplication下标识注解是同一个注解。
protected class<?> getspringfactoriesloaderfactoryclass() { return enableautoconfiguration.class;}
结论:获取一个能加载自动配置类的类,即springboot默认自动配置类为enableautoconfiguration
4.2.2 springfactoriesloader
springfactoriesloader工厂加载机制是spring内部提供的一个约定俗成的加载方式,只需要在模块的meta-inf/spring.factories文件,这个properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用springfactoriesloader来实现相应的实现类注入spirng容器中。
注:会加载所有jar包下的classpath路径下的meta-inf/spring.factories文件,这样文件不止一个。
4.2.2.1 loadfactorynames()
public static list<string> loadfactorynames(class<?> factorytype, @nullable classloader classloader) { cla熏肉的做法ssloader classloadertou = classloader; if (classloadertou == null) { classloadertou = springfactoriesloader.class.getclassloader(); } string factorytypename = factorytype.getname(); return loadspringfactories(classloadertou).getordefault(factorytypename, collections.emptylist());}
断点查看factorytypename:
先是将 enableautoconfiguration.class 传给了 factorytype
然后string factorytypename = factorytype.getname();,所以factorytypename 值为 org.springframework.boot.autoconfigure.enableautoconfiguration
4.2.2.2 loadspringfactories()
接着查看loadspringfactories方法的作用
private static map<string, list<string>> loadspringfactories(classloader classloader) { //断点查看 map<string, list<string>> result = cache.get(classloader); if (result != null) { return result; } result = new h魄罗头像ashmap<>(); try { //注意这里:meta-inf/spring.factories enumeration<url> urls = classloader.getresources(factories_resource_location); while (urls.hasmoreelements()) { url url = urls.nextelement(); urlresource resource = new urlresource(url); properties properties = propertiesloaderutils.loadproperties(resource); for (map.entry<?, ?> entry : properties.entryt()) { string factorytypename = ((string) entry.getkey()).trim(); string[] factoryimplementationnames = stringutils.commadelimitedlisttostringarray((string) entry.getvalue()); for (string factoryimplementationname : factoryimplementationnames) { //断点 result.computeifabnt(factorytypename, key -> new arraylist<>()) .add(factoryimplementationname.trim()); } } } // replace all lists with unmodifiable lists containing unique elements //去重,断点查看result值 result.replaceall((factorytype, implementations) -> implementations.stream().distinct() .collect(collectors.collectingandthen(collectors.tolist(), collections::unmodifiablelist))); cache.put(classloader, result); } catch (ioexception ex) { throw new illegalargumentexception("unable to load factories from location [" + factories_resource_location + "]", ex); } return result;}
这里的 factories_resource_location 在上面有定义:meta-inf/spring.factories
public final class springfactoriesloader { /** * the location to look for factories. * <p>can be prent in multiple jar files. */ public static final string factories_resource_location = "meta-inf/spring.factories";
meta-inf/spring.factories文件在哪里??
在所有引入的java包的当前类路径下的meta-inf/spring.factories文件都会被读取,如:
断点查看result值如下:
该方法作用是加载所有依赖的路径meta-inf/spring.factories文件,通过map结构保存,key为文件中定义的一些标识工厂类,value就是能自动配置的一些工厂实现的类,value用list保存并去重。
在回看 loadspringfactories(classloadertou).getordefault(factorytypename, collections.emptylist());
因为 loadfactorynames 方法携带过来的第一个参数为 enableautoconfiguration.class,所以 factorytype 值也为 enableautoconfiguration.class,那么 factorytypename 值为 enableautoconfiguration。拿到的值就是meta-inf/spring.factories文件下的key为
org.springframework.boot.autoconfigure.enableautoconfiguration的值
getordefault 当 map 集合中有这个 key 时,就使用这个 key值,如果没有就使用默认值空数组
结论:
loadspringfactories()该方法就是从“meta-inf/spring.factories”中加载给定类型的工厂实现的完全限定类名放到map中loadfactorynames()是根据springboot的启动生命流程,当需要加载自动配置类时,就会传入org.springframework.boot.autoconfigure.enableautoconfiguration参数,从map中查找key为org.springframework.boot.autoconfigure.enableautoconfiguration的值,这些值通过反射加到容器中,之后的作用就是用它们来做自动配置,这就是springboot自动配置开始的地方只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动当需要其他的配置时如监听相关配置:listenter,就传不同的参数,获取相关的listenter配置。在加载自动配置类的时候,并不是将spring.factories的配置全部加载进来,而是通过@conditional等注解的判断进行动态加载
@conditional其实是spring底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效。
常用的conditional注解:
@conditionalonclass : classpath中存在该类时起效@conditionalonmissingclass : classpath中不存在该类时起效@conditionalonbean : di容器中存在该类型bean时起效@conditionalonmissingbean : di容器中不存在该类型bean时起效@conditionalonsinglecandidate : di容器中该类型bean只有一个或@primary的只有一个时起效@conditionalonexpression : spel表达式结果为true时@conditionalonproperty : 参数设置或者值一致时起效@conditionalonresource : 指定的文件存在时起效@conditionalonjndi : 指定的jndi存在时起效@conditionalonjava : 指定的java版本存在时起效@conditionalonwebapplication : web应用环境下起效@conditionalonnotwebapplication : 非web应用环境下起效1.带有@configuration的配置类
2.importlector 的实现
3.importbeandefinitionregistrar 的实现
以上就是springboot自动配置原理详解的详细内容,更多关于springboot自动配置原理的资料请关注www.887551.com其它相关文章!
本文发布于:2023-04-04 07:46:36,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/161e2c49c123d35aad3a35b4a90b1b42.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:SpringBoot自动配置原理详解.doc
本文 PDF 下载地址:SpringBoot自动配置原理详解.pdf
留言与评论(共有 0 条评论) |