Springboot之自动装配原理

一、@SpringBootApplication

SpringBoot 的核心注解@SpringBootApplication可以看作是 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 注解的集合。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//启用 SpringBoot 的自动配置机制
@SpringBootConfiguration
//允许在上下文中注册额外的 bean 或导入其他配置类
@EnableAutoConfiguration
//扫描被@Component (@Service,@Controller)注解的 bean
//注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}

二、@EnableAutoConfiguration

@EnableAutoConfiguration 主要作用就是启用 SpringBoot 的自动配置机制,主要关注@Import({AutoConfigurationPackages.Registrar.class})

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//利用@Import(AutoConfigurationPackages.Registrar.class)来进行默认扫包行为(主程序所在包及其子包)
@AutoConfigurationPackage
//自动配置是否导入的选择器
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}

三、AutoConfigurationImportSelector

AutoConfigurationImportSelector类的ImportSelector选择器定义如下

//定义了一个selectImports方法用来选择哪些配置类可以加载
public interface ImportSelector {
    ...
    String[] selectImports(AnnotationMetadata importingClassMetadata);
    ...
}

//DeferredImportSelector是ImportSelector中的子接口
public interface DeferredImportSelector extends ImportSelector {
   ...
}

AutoConfigurationImportSelector实现了DeferredImportSelector接口


//AutoConfigurationImportSelector实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector, xxxx,xxxx {

//实现了ImportSelector方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //判断自动装配开关是否打开
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            //通过getAutoConfigurationEntry()方法,获取所有需要装配的bean
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
}
   
 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        //判断自动装配开关是否打开,默认spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml 中设置
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            //用于获取EnableAutoConfiguration注解中的exclude和excludeName。            
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

            //找出配置中的xxxAutoConfiguration,具体见下方
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

            //去重
            configurations = this.removeDuplicates(configurations);
            //过滤exclude和excludeName的
            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 AutoConfigurationEntry(configurations, exclusions);
        }
    }

经过上述方法就将需要加载的配置类,自动装配了!

备注:

  1. 从配置中获取自动配置类的方式

springboot2.7.0目前会向后兼容老版本配置模式spring.factories。

原配置方式

当前配置方式

META-INF/spring.factories

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@Configuration

@AutoConfiguration

新注解@AutoConfiguration是被用在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的自动化配置类上用来替换@Configuration@AutoConfigurationAfter@AutoConfigurationBefore注解,其中@Configuration对应的proxyBeanMethods属性值一直为false。

  1. 条件配置

  • @ConditionalOnBean:当容器里有指定 Bean 的条件下

  • @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下

  • @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean

  • @ConditionalOnClass:当类路径下有指定类的条件下

  • @ConditionalOnMissingClass:当类路径下没有指定类的条件下

  • @ConditionalOnProperty:指定的属性是否有指定的值

  • @ConditionalOnResource:类路径是否有指定的值

  • @ConditionalOnExpression:基于 SpEL 表达式作为判断条件

  • @ConditionalOnJava:基于 Java 版本作为判断条件

  • @ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置

  • @ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下

  • @ConditionalOnWebApplication:当前项目是 Web 项 目的条件下