『SpringBoot 源码分析』自动装配
基于 2.2.9.RELEASE 问题:Spring Boot 到底是如何进行自动配置的,都把哪些组件进行了自动配置?
首先创建测试主程序
package com. lagou ;
@SpringBootApplication
public class SpringBootMytestApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( SpringBootMytestApplication . class , args) ;
}
}
创建测试 Controller
package com. lagou. controller ;
@RestController
public class TestController {
@RequestMapping ( "/test" )
public String test ( ) {
System . out. println ( "源码环境构建成功..." ) ;
return "源码环境构建成功" ;
}
}
@SpringBootConfiguration
首先在主程序当中,我们通常会在主程序标记一个 @SpringBootApplication 注解
@SpringBootApplication
public class SpringBootMytestApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( SpringBootMytestApplication . class , args) ;
}
}
当点击进去注解之后,会发现注解是个组合注解,其中 @SpringBootConfiguration 、@EnableAutoConfiguration 、@ComponentScan 是 Spring 定义的
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan ( excludeFilters = { @Filter ( type = FilterType . CUSTOM , classes = TypeExcludeFilter . class ) ,
@Filter ( type = FilterType . CUSTOM , classes = AutoConfigurationExcludeFilter . class ) } )
public @interface SpringBootApplication {
@AliasFor ( annotation = EnableAutoConfiguration . class )
Class < ? > [ ] exclude ( ) default { } ;
@AliasFor ( annotation = EnableAutoConfiguration . class )
String [ ] excludeName ( ) default { } ;
@AliasFor ( annotation = ComponentScan . class , attribute = "basePackageClasses" )
Class < ? > [ ] scanBasePackageClasses ( ) default { } ;
@AliasFor ( annotation = Configuration . class )
boolean proxyBeanMethods ( ) default true ;
}
@SpringBootConfiguration :当我们再点击进去之后,会发现其实是个包装了 @Configuration 的注解,也就是说标注了 @SpringBootApplication 的类,其实是主配置类
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor ( annotation = Configuration . class )
boolean proxyBeanMethods ( ) default true ;
}
@EnableAutoConfiguration
上面只是阐述了 @SpringBootConfiguration 是标注主配置类的一个关键配置,但是并没有引入自动装配的组件,还有一个注解 @EnableAutoConfiguration ,用于启动自动配置功能
重点 :Spring 很多以 Enable 开头的注解,其作用是借助 @Import 来收集并注册特定场景的 bean,并加载到 IOC 容器中
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Inherited
@AutoConfigurationPackage
@Import ( AutoConfigurationImportSelector . class )
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ;
Class < ? > [ ] exclude ( ) default { } ;
String [ ] excludeName ( ) default { } ;
}
其中 @AutoConfigurationPackage 其实也是使用 @Import 导入组件 AutoConfigurationPackages.Registrar.class
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Inherited
@Import ( AutoConfigurationPackages. Registrar . class )
public @interface AutoConfigurationPackage {
}
当导入 AutoConfigurationPackages.Registrar.class 时,会调用 registerBeanDefinitions() 将信息注入到注册中心
public abstract class AutoConfigurationPackages {
. . .
static class Registrar implements ImportBeanDefinitionRegistrar , DeterminableImports {
@Override
public void registerBeanDefinitions ( AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register ( registry, new PackageImport ( metadata) . getPackageName ( ) ) ;
}
. . .
}
}
当调用 register() 方法,首先判断注册中心,是否已经包含名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的 BasePackages.class 的 beanDefinition ,没有的话,则创建,并且把包名 com.lagou 注册进去,供之后使用,例如给 JPA entity 的扫描器用来扫描开发人员通过注解 @Entity 定义的 entity 类
public abstract class AutoConfigurationPackages {
private static final String BEAN = AutoConfigurationPackages . class . getName ( ) ;
. . .
public static void register ( BeanDefinitionRegistry registry, String . . . packageNames) {
if ( registry. containsBeanDefinition ( BEAN ) ) {
BeanDefinition beanDefinition = registry. getBeanDefinition ( BEAN ) ;
ConstructorArgumentValues constructorArguments = beanDefinition. getConstructorArgumentValues ( ) ;
constructorArguments. addIndexedArgumentValue ( 0 , addBasePackages ( constructorArguments, packageNames) ) ;
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition ( ) ;
beanDefinition. setBeanClass ( BasePackages . class ) ;
beanDefinition. getConstructorArgumentValues ( ) . addIndexedArgumentValue ( 0 , packageNames) ;
beanDefinition. setRole ( BeanDefinition . ROLE_INFRASTRUCTURE ) ;
registry. registerBeanDefinition ( BEAN , beanDefinition) ;
}
}
. . .
static final class BasePackages {
. . .
}
}
@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 IOC 容器中。从继承的关系就可以看到,AutoConfigurationImportSelector 实现了各种 Aware,可以获取到 IOC 容器的关键配置。同时实现了 DeferredImportSelector 接口,而关键的自动配置相关逻辑,就在 DeferredImportSelectorGrouping.getImports() 方法里 我们先直接来看调用,首先 DeferredImportSelectorGrouping.getImports() 会遍历 deferredImports 属性,然后通过 DeferredImportSelector.Group 来处理自动装配
class ConfigurationClassParser {
. . .
private static class DeferredImportSelectorGrouping {
private final DeferredImportSelector. Group group;
private final List < DeferredImportSelectorHolder > deferredImports = new ArrayList < > ( ) ;
. . .
public Iterable < Group. Entry > getImports ( ) {
for ( DeferredImportSelectorHolder deferredImport : this . deferredImports) {
this . group. process ( deferredImport. getConfigurationClass ( ) . getMetadata ( ) ,
deferredImport. getImportSelector ( ) ) ;
}
return this . group. selectImports ( ) ;
}
}
}
当调用 process() 方法时,会先将 deferredImportSelector 转换为 AutoConfigurationImportSelector ,然后继续调用 getAutoConfigurationEntry() 方法
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware ,
ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
. . .
private static class AutoConfigurationGroup
implements DeferredImportSelector. Group , BeanClassLoaderAware , BeanFactoryAware , ResourceLoaderAware {
@Override
public void process ( AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
. . .
AutoConfigurationEntry autoConfigurationEntry = ( ( AutoConfigurationImportSelector ) deferredImportSelector)
. getAutoConfigurationEntry ( getAutoConfigurationMetadata ( ) , annotationMetadata) ;
. . .
}
}
}
接下来会调用 getCandidateConfigurations() 方法,去扫描所有的配置类
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware ,
ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
. . .
protected AutoConfigurationEntry getAutoConfigurationEntry ( AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
List < String > configurations = getCandidateConfigurations ( annotationMetadata, attributes) ;
. . .
}
}
接下来继续会去调用 SpringFactoriesLoader.loadFactoryNames() 方法,主要是去获取所有 spring.factories 中,key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有装配类
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware ,
. . .
ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
protected List < String > getCandidateConfigurations ( AnnotationMetadata metadata, AnnotationAttributes attributes) {
List < String > configurations = SpringFactoriesLoader . loadFactoryNames ( getSpringFactoriesLoaderFactoryClass ( ) ,
getBeanClassLoader ( ) ) ;
Assert . notEmpty ( configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct." ) ;
return configurations;
}
}
当调用 loadFactoryNames() 时,会先扫描所有的 META-INF/spring.factories 下的内容,然后拿到键为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有值,并返回
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;
. . .
public static List < String > loadFactoryNames ( Class < ? > factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType. getName ( ) ;
return loadSpringFactories ( classLoader) . getOrDefault ( factoryTypeName, Collections . emptyList ( ) ) ;
}
private static Map < String , List < String > > loadSpringFactories ( @Nullable ClassLoader classLoader) {
MultiValueMap < String , String > result = cache. get ( classLoader) ;
if ( result != null ) {
return result;
}
try {
Enumeration < URL> urls = ( classLoader != null ?
classLoader. getResources ( FACTORIES_RESOURCE_LOCATION ) :
ClassLoader . getSystemResources ( FACTORIES_RESOURCE_LOCATION ) ) ;
result = new LinkedMultiValueMap < > ( ) ;
while ( urls. hasMoreElements ( ) ) {
URL url = urls. nextElement ( ) ;
UrlResource resource = new UrlResource ( url) ;
Properties properties = PropertiesLoaderUtils . loadProperties ( resource) ;
for ( Map. Entry < ? , ? > entry : properties. entrySet ( ) ) {
String factoryTypeName = ( ( String ) entry. getKey ( ) ) . trim ( ) ;
for ( String factoryImplementationName : StringUtils . commaDelimitedListToStringArray ( ( String ) entry. getValue ( ) ) ) {
result. add ( factoryTypeName, factoryImplementationName. trim ( ) ) ;
}
}
}
cache. put ( classLoader, result) ;
return result;
}
catch ( IOException ex) {
throw new IllegalArgumentException ( "Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]" , ex) ;
}
}
}
META-INF/spring.factories 所在的位置 示例内容如下
当拿到自动装配类以后,回到 getAutoConfigurationEntry() 方法,继续执行一些过滤步骤,包括移除重新的配置类,排除 exclude 指定的类等等
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware ,
ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
. . .
protected AutoConfigurationEntry getAutoConfigurationEntry ( AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if ( ! isEnabled ( annotationMetadata) ) {
return EMPTY_ENTRY ;
}
AnnotationAttributes attributes = getAttributes ( annotationMetadata) ;
List < String > configurations = getCandidateConfigurations ( annotationMetadata, attributes) ;
configurations = removeDuplicates ( configurations) ;
Set < String > exclusions = getExclusions ( annotationMetadata, attributes) ;
checkExcludedClasses ( configurations, exclusions) ;
configurations. removeAll ( exclusions) ;
configurations = filter ( configurations, autoConfigurationMetadata) ;
fireAutoConfigurationImportEvents ( configurations, exclusions) ;
return new AutoConfigurationEntry ( configurations, exclusions) ;
}
}
我们简单看看 filter() 流程,核心点在于过滤所有 @ConditionOnxxx 的条件,例如 CacheAutoConfiguration ,它得存在相应的 bean 时,才会进行装配 完成过滤以后,返回到 process() 中,还会把返回的 AutoConfigurationEntry 装进 autoConfigurationEntries 属性集合中以及把符合条件的自动装配的类作为 key,存入 entries 属性集合中
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware ,
ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
. . .
private static class AutoConfigurationGroup
implements DeferredImportSelector. Group , BeanClassLoaderAware , BeanFactoryAware , ResourceLoaderAware {
@Override
public void process ( AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
. . .
AutoConfigurationEntry autoConfigurationEntry = ( ( AutoConfigurationImportSelector ) deferredImportSelector)
. getAutoConfigurationEntry ( getAutoConfigurationMetadata ( ) , annotationMetadata) ;
this . autoConfigurationEntries. add ( autoConfigurationEntry) ;
for ( String importClassName : autoConfigurationEntry. getConfigurations ( ) ) {
this . entries. putIfAbsent ( importClassName, annotationMetadata) ;
}
}
}
}
当处理完类的读取以后,DeferredImportSelectorGrouping 还需要执行一下 selectImports() 方法
class ConfigurationClassParser {
. . .
private static class DeferredImportSelectorGrouping {
private final DeferredImportSelector. Group group;
private final List < DeferredImportSelectorHolder > deferredImports = new ArrayList < > ( ) ;
. . .
public Iterable < Group. Entry > getImports ( ) {
for ( DeferredImportSelectorHolder deferredImport : this . deferredImports) {
this . group. process ( deferredImport. getConfigurationClass ( ) . getMetadata ( ) ,
deferredImport. getImportSelector ( ) ) ;
}
return this . group. selectImports ( ) ;
}
}
}
主要是对自动装配类再进行一次过滤,然后根据 @Order 进行排序
public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware ,
ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered {
. . .
private static class AutoConfigurationGroup
implements DeferredImportSelector. Group , BeanClassLoaderAware , BeanFactoryAware , ResourceLoaderAware {
private final Map < String , AnnotationMetadata > entries = new LinkedHashMap < > ( ) ;
private final List < AutoConfigurationEntry > autoConfigurationEntries = new ArrayList < > ( ) ;
. . .
@Override
public Iterable < Entry > selectImports ( ) {
if ( this . autoConfigurationEntries. isEmpty ( ) ) {
return Collections . emptyList ( ) ;
}
Set < String > allExclusions = this . autoConfigurationEntries. stream ( )
. map ( AutoConfigurationEntry :: getExclusions ) . flatMap ( Collection :: stream ) . collect ( Collectors . toSet ( ) ) ;
Set < String > processedConfigurations = this . autoConfigurationEntries. stream ( )
. map ( AutoConfigurationEntry :: getConfigurations ) . flatMap ( Collection :: stream )
. collect ( Collectors . toCollection ( LinkedHashSet :: new ) ) ;
processedConfigurations. removeAll ( allExclusions) ;
return sortAutoConfigurations ( processedConfigurations, getAutoConfigurationMetadata ( ) ) . stream ( )
. map ( ( importClassName) -> new Entry ( this . entries. get ( importClassName) , importClassName) )
. collect ( Collectors . toList ( ) ) ;
}
}
}
总结
源码流程 自动配置类总结
HttpEncodingAutoConfiguration
以 HttpEncodingAutoConfiguration 装配为例来简单再回归一下装配过程,首先肯定是去先扫描 META-INF/spring.factories 下所有的文件 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,经过一定条件过滤以后,再装入到容器当中。其中就包含了 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration 那 HttpEncodingAutoConfiguration 为什么没被过滤掉呢?我们简单看看它上面条件
@Configuration ( proxyBeanMethods = false )
@EnableConfigurationProperties ( HttpProperties . class )
@ConditionalOnWebApplication ( type = ConditionalOnWebApplication. Type . SERVLET )
@ConditionalOnClass ( CharacterEncodingFilter . class )
@ConditionalOnProperty ( prefix = "spring.http.encoding" , value = "enabled" , matchIfMissing = true )
public class HttpEncodingAutoConfiguration {
private final HttpProperties. Encoding properties;
public HttpEncodingAutoConfiguration ( HttpProperties properties) {
this . properties = properties. getEncoding ( ) ;
}
. . .
}
@EnableConfigurationProperties :该注解起到的作用是说把配置文件(如 application.yml )中的 spring.http
开头的属性,都装到 HttpProperties 类中。例如我们想设置 spring.http.encoding.charset=utf-8
,它就会自动装到对应属性中
@ConfigurationProperties ( prefix = "spring.http" )
public class HttpProperties {
private final Encoding encoding = new Encoding ( ) ;
. . .
public static class Encoding {
public static final Charset DEFAULT_CHARSET = StandardCharsets . UTF_8 ;
private Charset charset = DEFAULT_CHARSET ;
. . .
}
}
@ConditionalOnWebApplication :这个是条件之一,要求的是启动的时候,容器类型是 ConditionalOnWebApplication.Type.SERVLET ,只要在 pom.xml 配置了 spring-boot-starter-web,这一定会设置成这个类型@ConditionalOnClass 会判断 classpath 是否存在 CharacterEncodingFilter.class 这个类,只要在 pom.xml 配置了 spring-boot-starter-web,肯定会有这个类@ConditionalOnProperty 要求必须在 application.yml 中,配置了 spring.http.encoding.enabled
属性,但是属性上 matchIfMissing 允许缺失,所以这个条件也是通过的
满足了上面的所有条件后,自动装配类就没被过滤掉,加到容器中来