标签:limited 分析 转换 res active nbsp rmi lse framework
本文,我们来分享 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
|
标签:limited 分析 转换 res active nbsp rmi lse framework
原文地址:https://www.cnblogs.com/siye1989/p/11624838.html