Spring本质上就是一个管理程序应用的容器,而spring的一个核心功能就是自动装配,也就是在程序启动时就自动将应用所需的所有bean自动扫描、配置和装入到容器中去,方便程序的使用。

什么是SpringBoot自动装配?

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 应用在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中默认配置信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),然后我们就可以执行类中定义的各种操作了。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,也就是对应的starter,就能将自己的功能放入Spring容器中了。

当然,如果要修改第三方类库bean的配置的话,我们在application.yaml或application.properties中编写自定义的配置就可以了,spring在配置bean的时候会优先使用yaml或properties中的配置。

SpringBoot如何实现自动装配?

在我们应用的启动类中都要加上的一个注解 @SpringBootApplication ,这个复合注解就完成了应用中所有需要的bean的自动装配。@SpringBootApplication 包含了三个主要的注解,也就是
● @SpringBootConfiguration:标注这是一个SpringBoot应用级的配置类,允许在上下文中注册额外的 bean 或导入其他配置类
● @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
● @ComponentScan: 扫描被@Component (@Service,@Controller…等)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以使用exclude自定义不扫描某些 bean

其中**@EnableAutoConfiguration 是实现自动装配的核心注解类**,主要通过注解内的@AutoConfigurationPackage 和所导入的 AutoConfigurationImportSelector 类来实现。其中 @AutoConfigurationPackage 的作用是将主程序类所在包下的所有bean都注册到容器中,AutoConfigurationImportSelector 的作用是选择要注册到容器中的bean。

如何选择哪些组件是要加入容器中的?

那就要看AutoConfigurationImportSelector是怎么做的了。
AutoConfigurationImportSelector 的类依赖链大致是这样的:
AutoConfigurationImportSelector -> DeferredImportSelector -> ImportSelector

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

}

public interface DeferredImportSelector extends ImportSelector {

}

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

AutoConfigurationImportSelector实现了 ImportSelector 中的 selectImports() 方法:

private static final String[] NO_IMPORTS = new String[0];

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // <1>.判断自动装配开关是否打开
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
    //<2>.获取所有需要装配的bean
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

需要重点关注的是 selectImport 方法中 获取所有需要装配组件 getConfigurationEntry() 方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    //(1)获取元注解中的所有属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //(2)获取元注解中所有属性的配置项,这些作为候选配置项
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    //(3)去除重复的配置项
    configurations = removeDuplicates(configurations);
    //(4)去除用户自定义的需要去除的配置项
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

1. 获取元注解中的所有属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
这一步是获取元注解中的所有属性被赋予的配置项,也就是我们在 @EnableAutoConfiguration 填充的参数。

2. 获取元注解中所有属性的配置项,这些作为候选配置项

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;
}

Spring 会去加载所有 Spring Boot Starter 下的META-INF/spring.factories 文件中的类,这个文件中定义了第三方jar包加载的所有所需配置类。
比如这是spring-boot-starter-mybatis的META-INF/spring.factories:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

是所有的配置项都会加载吗?
不是的,Spring中有许多的条件注解,比如@ConditionalOnClass()、@ConditionalOnMissingBean、@ConditionalOnMissingBean等,某个配置类的bean需要满足某种条件才会进行加载。比如@ConditionalOnMissingBean会限制某个bean只会在没有同类型的bean的时候才会进行装载,保证容器中只有一个同类型的bean。

下面两个就不用多说了:

3. 去除重复的配置项
configurations = removeDuplicates(configurations);

4. 去除用户自定义的需要去除的配置项
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);

参考资料:
淘宝一面:“说一下 Spring Boot 自动装配原理呗?

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐