实现starter组件自动装配以及可配置

发布时间 2023-10-09 10:24:55作者: 我不想学编丿程

实现starter组件自动装配以及可配置

自动装配的包,导入就可以进行自动装配了

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>

配置文件代码提示

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

实现starter组件自动装配以及可配置

Spring Boot自动装配原理

前面说过Spring Boot的特点就是自动装配,相信使用过Spring的读者们都体会过XML配置的复杂,而使用Spring Boot会发现除了导入依赖之外,无需过多的配置即可使用,就算需要配置时也只需要通过配置类或配置文件进行简单配置即可,这样的便利归功于Spring Boot的自动装配。接下来就了解一下Spring Boot究竟如何实现了自动装配。

学过Java的应该都听过SPI机制(JDK内置的一种服务发现机制),而Spring boot正是基于SPI机制的方式对外提供了一套接口规范(当Spring Boot项目启动时,会扫描外部引入的Jar中的META-INF/spring.factories文件,将文件中配置的类信息装配到Spring容器中),让引入的Jar实现这套规范即可自动装配进Spring Boot中。

所以想要自定义的starter组件实现自动装配,只需要在项目的 resources 中创建 META-INF 目录,并在此目录下创建一个 spring.factories 文件,将starter组件的自动配置类的类路径写在文件上即可。

项目代码-META-INF/spring.factories

// 添加项目的自动配置类的类路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.lucas.check.config.CheckAutoConfigure

虽然已经解决了自定义starter实现自动装配,但是有兴趣的还是接着了解一下底层自动装配的过程。那么Spring Boot是如何找到 META-INF/spring.factories 的文件并进行自动配置的呢?

深入源码解析自动装配过程

  1. Spring Boot项目在启动类上都会有一个 @SpringBootApplication 注解,这个注解可以看作是@Configuration@EnableAutoConfiguration@ComponentScan的集合,而自动装配原理就在其中的 @EnableAutoConfiguration 中,这个注解作用是开启自动配置机制。

  2. @EnableAutoConfiguration 中实现自动装配核心功能的是@Import,通过加载自动装配类AutoConfigurationImportSelector实现自动装配功能。

  3. 深入 AutoConfigurationImportSelector 代码发现该类实现了ImportSelector接口同时实现了该接口的selectImports方法,这个方法用来获取所有符合条件的类的全限定名(为什么是符合条件,只在后续按需装配中说明),并且将这些类加载进Spring容器中。

  4. 再深入

    selectImports
    

    方法中会发现这个

    getAutoConfigurationEntry()
    

    方法才是最终获取到

    META-INF/spring.factories

    文件的方法。

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        // 第一步:先判断是否开启了自动装配机制
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            // 第二步:获取@SpringBootApplication中需要排除的类(exclude和excludeName属性),通过这两个属性排除指定的不需要自动装配的类
            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);
        }
    }
    

到这里便是SpringBoot的自动装配原理整个过程(可能描述的不完整,有兴趣的可以去找找资料看看)。

抛出问题:刚刚提到的Spring Boot只会加载符合条件的类是怎么回事?

Spring Boot其实并不会加载 META-INF/spring.factories 文件下的所有类,而是按需加载,怎么个按需加载呢?

Spring Boot可以通过@ConditionalOnXXX满足条件时加载(即按需加载),下面列举一些常用的注解:

  • @ConditionalOnBean:当容器里存在指定Bean时,实例化(加载)当前Bean。
  • @ConditionalOnMissingBean:当容器里不存在指定Bean时,实例化(加载)当前Bean。
  • @ConditionalOnClass:当类路径下存在指定类时,实例化(加载)当前Bean。
  • @ConditionalOnMissingClass:当类路径下不存在指定类时,实例化(加载)当前Bean。
  • @ConditionalOnProperty:配置文件中指定的value属性是指定的havingValue属性值时,实例化(加载)当前Bean。