SpringBoot2的自动配置原理

       今天学习了SpringBoot2的自动配置原理,认为十分巧妙,所以记录一下自己的学习感悟。

1 预备知识

        在介绍SpringBoot2的自动装配原理之前,有几个注解需要我们认识一下,这几个注解在我看来是自动装配原理的基础。

       1.1 @Conditional 条件装配

        这里的注解并不只有@Conditional,而是和@Conditional具有相同功能的注解,比如以下注解:

SpringBoot2的自动配置原理

         此类注解的作用是:当满足Conditional指定的条件时,才进行组件的注入。

       1.2 @Import

         向容器中导入指定类型的组件

2 自动配置原理

       2.1 自动加载配置类

          进入SpringBoot2的启动类可以看到三个比较重要的注解,如下:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
)

          其中,@SpringBootConfiguration标注这是一个配置类,@ComPonentScan即要扫描那些包。这三个中最重要的即是@EnableAutoConfiguration注解。源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
====以下较为重要======
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {

        @AutoConfigurationPackager

          重要部分源码如下:

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

        其中@Import({Registrar.class})给容器中导入Registrar类型的组件。

        进入Register中可以看到以下方法

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        其中new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames()作用是得到主类的包名,然后将其封装到一个数组中,之后register扫描此包下所有符合条件的组件,再注册进容器中。

        所以说@AutoConfigurationPackager注解,作用是将指定的一个包下的组件导入容器中,默认为启动类所在的包下,这也说明了在没有修改相关配置时,我们要将其他组件写在启动类的目录下。

       @Import({AutoConfigurationImportSelector.class})

        此注解导入了AutoConfigurationImportSelector类型的组件,进入AutoConfigurationImportSelector代码中可以看到下面的方法:

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

                在List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes)打断点可以得到:

SpringBoot2的自动配置原理

              由此可见,此方法是用来获取需要导入到容器的组件。再进入此方法可以得到最关键的方法,名为loadSpringFactories,源码太多,就不全部贴了。我们可以在loadSpringFactories的源码中看到此语句Enumeration urls = classLoader.getResources("META-INF/spring.factories")  目的是加载META-INF/spring.factories文件,默认扫描我们当前系统中所有META-INF/spring.factories文件。

               且spring-boot-autoconfigure-2.6.1.jar包中也有META-INF/spring.factories,此文件中写死了SpringBoot一启动就要给容器中加载的所有配置类,

          2.2 按需开启自动配置项

               上面说了META-INF/spring.factories文件中的所有场景的自动配置在启动的时候默认全部加载,但最终配置时并没有那么多,因为在配置时会按需配置,有些组件并不会配置。

              在SpringBoot2中,按需配置的解决方法主要是使用@Conditional及其有相同功能的注解

下面举个例子:
             
例如SpringBoot2中的Aop中使用了以下注解

    @ConditionalOnMissingClass({"org.aspectj.weaver.Advice"})

              即有Advice类才会把Aop放入容器中。

3 SpringBoot2的自动装配流程

             即启动时加载META-INF/spring.factories中所有的配置,但根据@Conditional及其有相同功能的注解使它们并不能全部生效,这样来保证按需装配。

这样来看,如果不被成千上万条源码吓到的话,SpringBoot2的自动装配原理挺简单的,加油。学习无止境。

上一篇:RabbitMQ 入门 (Go) - 5. 使用 Fanout Exchange 做服务发现(下)


下一篇:Freemarker源码分析(5)cache包中其他的类