本文,我们来分享 Spring Boot 自动配置的实现源码。在故事的开始,我们先来说两个事情:
在这篇文章的开始,艿艿是有点混淆自动配置和自动装配的概念,后来经过 Google 之后,发现两者是截然不如同的:
spring-boot-starter-web 之后,就自动引入了 Spring MVC 相关的 jar 包,从而自动配置 Spring MVC 。所以,不要和艿艿一样愚蠢的搞错落。
胖友可以直接看 《详解 Spring Boot 自动配置机制》 文章的 「二、Spring Boot 自动配置」 小节,艿艿觉得写的挺清晰的。
下面,我们即开始正式撸具体的代码实现了。
org.springframework.boot.autoconfigure.@SpringBootApplication 注解,基本我们的 Spring Boot 应用,一定会去有这样一个注解。并且,通过使用它,不仅仅能标记这是一个 Spring Boot 应用,而且能够开启自动配置的功能。这是为什么呢?
??
@SpringBootApplication注解,它在spring-boot-autoconfigure模块中。所以,我们使用 Spring Boot 项目时,如果不想使用自动配置功能,就不用引入它。当然,我们貌似不太会存在这样的需求,是吧~
@SpringBootApplication 是一个组合注解。代码如下:
// SpringBootApplication.java
|
下面,我们来逐个看 @SpringBootApplication 上的每个注解。
Java 自带的注解。
java.lang.annotation.@Inherited 注解,使用此注解声明出来的自定义注解,在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则的话,子类不会继承此注解。
这里一定要记住,使用 @Inherited 声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。
不了解的胖友,可以看看 《关于 Java 注解中元注解 Inherited 的使用详解》 文章。
Spring Boot 自定义的注解
org.springframework.boot.@SpringBootConfiguration 注解,标记这是一个 Spring Boot 配置类。代码如下:
// SpringBootConfiguration.java
|
@Configuration 注解,所以两者功能也一致,可以将当前类内声明的一个或多个以 @Bean 注解标记的方法的实例纳入到 Srping 容器中,并且实例名就是方法名。Spring 自定义的注解
org.springframework.context.annotation.@ComponentScan 注解,扫描指定路径下的 Component(@Componment、@Configuration、@Service 等等)。
不了解的胖友,可以看看 《Spring:@ComponentScan 使用》 文章。
Spring Boot 自定义的注解
org.springframework.boot.autoconfigure.@EnableAutoConfiguration 注解,用于开启自动配置功能,是 spring-boot-autoconfigure 项目最核心的注解。代码如下:
// EnableAutoConfiguration.java
|
org.springframework.boot.autoconfigure.@AutoConfigurationPackage 注解,主要功能自动配置包,它会获取主程序类所在的包路径,并将包路径(包括子包)下的所有组件注册到 Spring IOC 容器中。代码如下:
// AutoConfigurationPackage.java
|
org.springframework.context.annotation.@Import 注解,可用于资源的导入。情况比较多,可以看看 《6、@Import 注解——导入资源》 文章。@Import(AutoConfigurationImportSelector.class) 注解部分,是重头戏的开始。
org.springframework.context.annotation.@Import 注解,可用于资源的导入。情况比较多,可以看看 《6、@Import 注解——导入资源》 文章。org.springframework.boot.autoconfigure.AutoConfigurationImportSelector ,实现 DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware、Ordered 接口,处理 @EnableAutoConfiguration 注解的资源导入。
#getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法,获得符合条件的配置类的数组。代码如下:
// AutoConfigurationImportSelector.java
|
<1> 处,调用 #getSpringFactoriesLoaderFactoryClass() 方法,获得要从 META-INF/spring.factories 加载的指定类型为 EnableAutoConfiguration 类。代码如下:
// AutoConfigurationImportSelector.java
|
<1> 处,调用 SpringFactoriesLoader#loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) 方法,加载指定类型 EnableAutoConfiguration 对应的,在 META-INF/spring.factories 里的类名的数组。看看下图,胖友相信就明白了:
一般来说,和网络上 Spring Boot 敢于这块的源码解析,我们就可以结束了。如果单纯是为了了解原理 Spring Boot 自动配置的原理,这里结束也是没问题的。因为,拿到 Configuration 配置类后,后面的就是 Spring Java Config 的事情了。不了解的胖友,可以看看 《Spring 教程 —— 基于 Java 的配置》 文章。
?? 但是(“但是”同学,你赶紧坐下),具有倒腾精神的艿艿,觉得还是继续瞅瞅 #getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法是怎么被调用的。所以,我们来看看调用它的方法调用链,如下图所示:
#getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法被调用。③ 处,那么此处,就是问题的关键。代码如下:
艿艿:因为我还没特别完整的撸完 Spring Java Annotations 相关的源码,所以下面的部分,我们更多是看整个调用过程。?? 恰好,胖友也没看过,哈哈哈哈。
// ConfigurationClassParser#DeferredImportSelectorGroupingHandler.java
|
<1> 处,调用 DeferredImportSelector.Group#process(AnnotationMetadata metadata, DeferredImportSelector selector) 方法,处理被 @Import 注解的注解。<2> 处,调用 DeferredImportSelector.Group#this.group.selectImports() 方法,选择需要导入的。例如:
<1> 和 <2> 处,在 「5.3 AutoConfigurationGroup」 详细解析。#getImportGroup() 方法,获得对应的 Group 实现类。代码如下:
// AutoConfigurationImportSelector.java
|
艿艿:注意,从这里开始后,东西会比较难。因为,涉及的东西会比较多。
AutoConfigurationGroup ,是 AutoConfigurationImportSelector 的内部类,实现 DeferredImportSelector.Group、BeanClassLoaderAware、BeanFactoryAware、ResourceLoaderAware 接口,自动配置的 Group 实现类。
// AutoConfigurationImportSelector#AutoConfigurationGroup.java
|
entries 属性,AnnotationMetadata 的映射。其中,KEY 为 配置类的全类名。在后续我们将看到的 AutoConfigurationGroup#process(...) 方法中,被进行赋值。例如:
autoConfigurationEntries 属性,AutoConfigurationEntry 的数组。
其中,AutoConfigurationEntry 是 AutoConfigurationImportSelector 的内部类,自动配置的条目。代码如下:
// AutoConfigurationImportSelector#AutoConfigurationEntry.java
|
AutoConfigurationGroup#process(...) 方法中,被进行赋值。例如:
autoConfigurationMetadata 属性,自动配置的元数据(Metadata)。
通过 #getAutoConfigurationMetadata() 方法,会初始化该属性。代码如下:
// AutoConfigurationImportSelector#AutoConfigurationGroup.java
|
@ConditionalOnClass({ Bucket.class, ReactiveCouchbaseRepository.class, Flux.class }) 注解部分。autoConfigurationMetadata 属性,用途就是制定配置类(Configuration)的生效条件(Condition)。#process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) 方法,进行处理。代码如下:
// AutoConfigurationImportSelector#AutoConfigurationGroup.java
|
annotationMetadata 参数,一般来说是被 @SpringBootApplication 注解的元数据。因为,@SpringBootApplication 组合了 @EnableAutoConfiguration 注解。deferredImportSelector 参数,@EnableAutoConfiguration 注解的定义的 @Import 的类,即 AutoConfigurationImportSelector 对象。<1> 处,调用 AutoConfigurationImportSelector#getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法,获得 AutoConfigurationEntry 对象。详细解析,见 「5.4 AutoConfigurationEntry」 。因为这块比较重要,所以先跳过去瞅瞅。<2> 处,添加到 autoConfigurationEntries 中。<3> 处,添加到 entries 中。#selectImports() 方法,获得要引入的配置类。代码如下:
// AutoConfigurationImportSelector#AutoConfigurationGroup.java
|
<1> 处,如果为空,则返回空数组。<2.1>、<2.2>、<2.3> 处,获得要引入的配置类集合。?? 比较奇怪的是,上面已经做过一次移除的处理,这里又做一次。不过,没多大关系,可以先无视。<3> 处,处理,返回结果。
<3.1> 处,调用 #sortAutoConfigurations(Set<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) 方法,排序。代码如下:
// AutoConfigurationImportSelector#AutoConfigurationGroup.java
|
@Order 注解。<3.2> 处,创建 Entry 对象。<3.3> 处,转换成 List 。结果如下图:
艿艿:略微有点艰难的过程。不过回过头来,其实也没啥特别复杂的逻辑。是不,胖友~
艿艿:这是一个关键方法。因为会调用到,我们会在 「5.1 getCandidateConfigurations」 的方法。
#getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法,获得 AutoConfigurationEntry 对象。代码如下:
// AutoConfigurationImportSelector.java
|
这里每一步都是细节的方法,所以会每一个方法,都会是引导到对应的小节的方法。
虽然有点长,但是很不复杂。简单的来说,加载符合条件的配置类们,然后移除需要排除(exclusion)的。
<1> 处,调用 #isEnabled(AnnotationMetadata metadata) 方法,判断是否开启。如未开启,返回空数组。详细解析,见 「5.4.1 isEnabled」 。<2> 处,调用 #getAttributes(AnnotationMetadata metadata) 方法,获得注解的属性。详细解析,见 「5.4.2 getAttributes」 。【重要】<3> 处,调用 #getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法,获得符合条件的配置类的数组。
嘻嘻,到达此书之后,整个细节是不是就串起来了!
<3.1> 处,调用 #removeDuplicates(List<T> list) 方法,移除重复的配置类。代码如下:
// AutoConfigurationImportSelector.java
|
<4> 处,调用 #getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法,获得需要排除的配置类。详细解析,见 「5.4.3 getExclusions」 。
<4.1> 处,调用 #checkExcludedClasses(List<String> configurations, Set<String> exclusions) 方法,校验排除的配置类是否合法。详细解析,见 「5.4.4 checkExcludedClasses」 。<4.2> 处,从 configurations 中,移除需要排除的配置类。<5> 处,调用 #filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) 方法,根据条件(Condition),过滤掉不符合条件的配置类。详细解析,见 《精尽 Spring Boot 源码分析 —— Condition》 文章。
<6> 处,调用 #fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) 方法,触发自动配置类引入完成的事件。详细解析,见 「5.4.5 fireAutoConfigurationImportEvents」 。<7> 处,创建 AutoConfigurationEntry 对象。整个 「5.4 getAutoConfigurationEntry」 看完后,胖友请跳回 「5.3.3 selectImports」 。
#isEnabled(AnnotationMetadata metadata) 方法,判断是否开启自动配置。代码如下:
// AutoConfigurationImportSelector.java
|
#getAttributes(AnnotationMetadata metadata) 方法,获得注解的属性。代码如下:
// AutoConfigurationImportSelector.java
|
getAnnotationClass().getName() 返回的是 @EnableAutoConfiguration 注解,所以这里返回的注解属性,只能是 exclude 和 excludeName 这两个。举个例子,假设 Spring 应用上的注解如下:
|

#getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法,获得需要排除的配置类。代码如下:
// AutoConfigurationImportSelector.java
|
该方法会调用如下的方法,比较简单,胖友自己瞅瞅。
// AutoConfigurationImportSelector.java
|
#checkExcludedClasses(List<String> configurations, Set<String> exclusions) 方法,校验排除的配置类是否合法。代码如下:
// AutoConfigurationImportSelector.java
|
exclusions 存在于 classpath 中,但是不存在 configurations 。这样做的目的是,如果不存在的,就不要去排除啦!#fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) 方法,触发自动配置类引入完成的事件。代码如下:
// AutoConfigurationImportSelector.java
|
<1> 处,调用 #getAutoConfigurationImportListeners() 方法,加载指定类型 AutoConfigurationImportListener 对应的,在 META-INF/spring.factories 里的类名的数组。例如:
<2> 处,创建 AutoConfigurationImportEvent 事件。<3> 处,遍历 AutoConfigurationImportListener 监听器们,逐个通知。
<3.1> 处,调用 #invokeAwareMethods(Object instance) 方法,设置 AutoConfigurationImportListener 的属性。代码如下:
// AutoConfigurationImportSelector.java
|
<3.2> 处,调用 AutoConfigurationImportListener#onAutoConfigurationImportEvent(event) 方法,通知监听器。目前只有一个 ConditionEvaluationReportAutoConfigurationImportListener 监听器,没啥逻辑,有兴趣自己看哈。
org.springframework.boot.autoconfigure.AutoConfigurationPackages ,自动配置所在的包名。可能这么解释有点怪怪的,我们来看下官方注释:
Class for storing auto-configuration packages for reference later (e.g. by JPA entity scanner).
|
@AutoConfigurationPackage 注解的类所在的包(package),注册成一个 Spring IoC 容器中的 Bean 。酱紫,后续有其它模块需要使用,就可以通过获得该 Bean ,从而获得所在的包。例如说,JPA 模块,需要使用到。是不是有点神奇,艿艿也觉得。
Registrar ,是 AutoConfigurationPackages 的内部类,实现 ImportBeanDefinitionRegistrar、DeterminableImports 接口,注册器,用于处理 @AutoConfigurationPackage 注解。代码如下:
// AutoConfigurationPackages#Registrar.java
|
PackageImport 是 AutoConfigurationPackages 的内部类,用于获得包名。代码如下:
// AutoConfigurationPackages#Registrar.java
|

<X> 处,调用 #register(BeanDefinitionRegistry registry, String... packageNames) 方法,注册一个用于存储报名(package)的 Bean 到 Spring IoC 容器中。详细解析,见 「6.2 register」 。#register(BeanDefinitionRegistry registry, String... packageNames) 方法,注册一个用于存储报名(package)的 Bean 到 Spring IoC 容器中。代码如下:
// AutoConfigurationPackages.java
|
注册的 BEAN 的类型,为 BasePackages 类型。它是 AutoConfigurationPackages 的内部类。代码如下:
// AutoConfigurationPackages#BasePackages.java
|
packages 属性的封装类。<1> 处,如果已经存在该 BEAN ,则修改其包(package)属性。而合并 package 的逻辑,通过 #addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) 方法,进行实现。代码如下:
// AutoConfigurationPackages.java
|
<2> 处,如果不存在该 BEAN ,则创建一个 Bean ,并进行注册。#has(BeanFactory beanFactory) 方法,判断是否存在该 BEAN 在传入的容器中。代码如下:
// AutoConfigurationPackages.java
|
#get(BeanFactory beanFactory) 方法,获得 BEAN 。代码如下:
// AutoConfigurationPackages.java
|
原文:https://www.cnblogs.com/siye1989/p/11624838.html