首先,你在pom文件里引入的很多第三方jar里都有一个文件 META-INF/spring.factories,这个文件里的内容和关系到能否自动配置, 那有的jar为啥没有,是不需要SpringBoot来自动配置吗,这个我们后面再说。
先来看一下redisson starter的/ META-INF/spring.factories是怎么写的, 如下图,只有一行,是key=value的格式
这个key是一个spring的注解,value是一个redission的jar包里的一个配置类, 如下图
根据注解的名字,大胆猜测一下,这个配置类是需要被自动配置的
事实上就是这样的,SpringBoot会扫描所有classpath下的所有jar里面的spring.factories文件,然后生成一个大概就是Map<String, List<String>>形式的一个键值对,value会和相同key的value组成一个List。
那就可以根据key是EnableAutoConfiguration,得到所有需要自动配置的配置类的信息了,到时候只需要把每个配置类配置一下就行了
具体怎么做的呢?
首先我们SpringBoot项目默认都是有Application类,上面还有个注解@SpringBootApplication,这是个组合注解,是由好多个别的注解组合而成,因为我们是想研究自动配置,那我们着重关注
@SpringBootConfiguration 这个注解代表这个是源配置文件
@EnableAutoConfiguration 这个代表Spring能进行自动配置,既是一个开关,也是指引Spring怎么去找到并进行自动配置, 它本身也是个组合注解,点开来看又用@import注解引入了一个class
@ComponentScan 这个会告诉Spring扫描哪些package下的类,这里默认会扫描Application所在的package下
打开AutoConfigurationImportSelector.class看, 它实现了接口 DeferredImportSelector,DeferredImportSelector接口又是继承自ImportSelector,
DeferredImportSelector翻译一下就是 推迟的引入选择器,从名字上来看,它会帮助Spring引入bean或者配置,而且是推迟的
这是因为Spring会先配置好我们代码中的(即和Application类相同package下的)配置类,然后把我们写的一些bean交给IoC来管理,然后才是引入依赖的配置类,并把依赖里面的bean给实例化并交给IoC。
为啥要这么先处理自己代码包下的配置类和bean?网上找了一下答案,说是方便我们扩展和覆盖。好像没毛病,肯定是依赖里面帮你配置好了bean,如果你自己不配置的话,就直接用依赖包里写好的,但是你如果自己写了,依赖包里配置好的bean就不使用了。符合SpringBoot的约定优于配置。
比如我们如果引入了redisson的starter并在application.properties里面配置一下连接参数,就能在Service中或者Component中使用@Resource或者@Autowired来注入RedissonClient这个bean了,这个bean是怎么会被IoC容器实例化并存在容器中的呢?
我们再次打开redisson--spring-boot-starter的spring.factories和它里面指定的RedissonAutoConfiguration这个配置类来看一下,如下图
通过SpringBoot的自动配置,先把RedissonAutoConfiguration这个配置类通过spring.factories暴露给Spring,然后Spring根据这个配置类,调用了下面很多个@Bean注解的方法,把这些实例给加到IoC容器中。
@ConditionalOnMissionBean(RedissonClient.class)的意思就是如果我们自己的代码中没有引入这个bean,那就会帮我们把RedissonClient给实例化并交给IoC,
假如我们自己想要自己实例化一个特殊的RedissonClient或者在实例化前做点操作,那我们可以自己写一下,如下图,这样也就是实现了上面提到的 方便我们扩展和覆盖
回到上面的一个问题,为什么有的starter里面没有spring.factories呢?那它是怎么实现自动配置的。
是因为有个我们把我们的项目的parent设置成了spring-boot-starter-parent,springboot自动帮我们依赖了spring-boot-autoconfigure.jar
这里面有两个重要的文件,一个是配置了当前SpringBoot版本常用的依赖的配置类,一个是配置类的一些加载条件,你看我在里面找到了啥,是spring-boot-starter-data-redis里面的配置类,之所以starter的jar里面没有spring.factories文件,原来是在这里统一配置了。
最后我们再来看一下调用的大概过程
从@SpringBootApplication =》 @EnableAutoConfiguration =》 @Import({AutoConfigurationImportSelector.class}),看一下这个class的一个重要方法getAutoConfigurationEntry
这个方法就是放回所有的配置类,先通过SpringFactoriesLoader去获取依赖的jar里的配置类,然后获取了从autoconfigure这个jar的AutoCOnfiguration.imports文件里找到的所有配置类,加在一起作为候选名单,经过去重,filter(根据spring-autoconfiguration-metadata.properties里的条件)等操作,返回出去需要自动配置的一些配置类。
然后在引入这些配置类的时候,根据@Conditional***的注解,按要求把需要的bean都给实例化加到IoC容器。
自动配置大概就是这样。
提一下条件配置,随便裂了几个常见的
ConditionalOnMissingBean 如果后面指定的这个bean还没有加入到IoC容器的话,就把这个注解标注的方法执行一下
ConditionalOnMissingClass 同上,只不过变成了class,猜测应该是有没有类加载器加载了这个class
ConditionalOnBean 某个bean已经被加入IoC了,才能执行这个方法
ConditionalOnClass 某个class已经被类加载了,才能执行这个方法
SPI和spring.factories其实比较像,都是约定了一个文件位置,然后读这个文件来实现自动扩展,动态加载一些内容。有兴趣的自己查一下。
标签:springboot,spring,配置,jar,bean,自动,注解 From: https://www.cnblogs.com/huainanyin/p/18073942