Spring Boot 自动装配原理
1. SpringBoot的两大核心
Spring Boot 框架的两大核心特性可以概括为“启动器”(Starter)和“自动配置”(Auto-configuration)。
启动器(Starter): Spring Boot 提供了预定义的依赖关系。当你在项目中引入一个 Starter POM 时,它会自动包含所有必要的 Spring 组件以及合理的默认设置。开发者不需要手动管理复杂的依赖关系,也不需要担心版本冲突的问题,减少了配置上的出错可能。比如:如果项目中添加了 spring-boot-starter-web,Spring Boot 会自动配置 Tomcat 和 Spring MVC。
自动配置(Auto-Configuration): 当添加了特定的 Starter POM 后,Spring Boot 会根据类路径上存在的 jar 包来自动配置 Bean(自动配置相关组件)(比如:SpringBoot发现类路径上存在mybatis相关的类,例如SqlSessionFactory.class,那么SpringBoot将自动配置mybatis相关的所有Bean。)。
这两个特性结合在一起,使得使用 Spring Boot 开发应用程序变得更加简单快速,减少了大量的样板代码和重复配置的工作。让程序员专注业务逻辑的开发,在环境方面耗费最少的时间。
导入了spring-boot-starter-web
【web启动器】会关联导入一个jar包:spring-boot-autoconfigure
2.自动装配实现原理
2.1 Spring注解
在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
2.2 Spring类导入
点进去@EnableAutoConfiguration里面有一个@Import({AutoConfigurationImportSelector.class})
main 类启动的时候,Spring Boot 会通过底层的AutoConfigurationImportSelector
类加载自动装配类。
@AutoConfigurationPackage //将main同级的包下的所有组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
AutoConfigurationImportSelector
实现了ImportSelector
接口,该接口的作用是收集需要导入的配置类,配合 @Import()
将相应的类导入到 Spring 容器中。
2.3 AutoConfigurationSelector
获取注入类的方法是 selectImports()
,它实际调用的是getAutoConfigurationEntry()
点进去getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 检查自动配置是否启用。如果@ConditionalOnClass等条件注解使得自动配置不适用于当前环境,则返回一个空的配置条目。
//@ConditionalOnClass注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取启动类上的@EnableAutoConfiguration注解的属性,这可能包括对特定自动配置类的排除。
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从spring.factories中获取所有候选的自动配置类。这是通过加载META-INF/spring.factories文件中对应的条目来实现的。
// getCandidateConfigurations方法在后面说明
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 移除配置列表中的重复项,确保每个自动配置类只被考虑一次。
configurations = removeDuplicates(configurations);
// 根据注解属性解析出需要排除的自动配置类。
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查排除的类是否存在于候选配置中,如果存在,则抛出异常。
checkExcludedClasses(configurations, exclusions);
// 从候选配置中移除排除的类。
configurations.removeAll(exclusions);
// 应用过滤器进一步筛选自动配置类。过滤器可能基于条件注解如@ConditionalOnBean等来排除特定的配置类。
configurations = getConfigurationClassFilter().filter(configurations);
// 触发自动配置导入事件,允许监听器对自动配置过程进行干预。
fireAutoConfigurationImportEvents(configurations, exclusions);
// 创建并返回一个包含最终确定的自动配置类和排除的配置类的AutoConfigurationEntry对象。
return new AutoConfigurationEntry(configurations, exclusions);
}
补充:getCandidateConfigurations通过SpringFactoriesLoader 从 META-INF/spring.factories加载配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 1. 通过SpringFactoriesLoader从META-INF/spring.factories加载配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
// getSpringFactoriesClass() 返回EnableAutoConfiguration.class,指定要加载的配置键
getSpringFactoriesClass(),
// getBeanClassLoader() 提供类加载器用于读取文件
getBeanClassLoader()
);
// 2. 校验配置列表是否为空(防止文件缺失或配置错误)
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."
);
总结:
@EnableAutoConfiguration
是实现自动化配置的核心注解。
这个注解里面有一个@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector
实现了ImportSelector
接口,该接口的作用是收集需要导入的配置类,配合 @Import()
将相应的类导入到 Spring 容器中。
AutoConfigutationImportSelector
获取注入类的方法是 selectImports()
,它实际调用的是getAutoConfigurationEntry()
。
getAutoConfigurationEntry()
方法里面有个getCandidateConfigurations
方法,通过SpringFactoriesLoader 从 META-INF/spring.factories加载配置类
在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
一般条件判断会有像@ConditionalOnClass
这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。