Springboot基础篇(4):自动配置原理
1 自动配置原理剖析
1.1 加载配置类的源码追溯
- 自动配置的触发入口:
@SpringBootApplication
组合注解是自动配置的起点,其核心包含@EnableAutoConfiguration
,该注解使用AutoConfigurationImportSelector
实现配置类的动态加载。
//可以应用于类、接口(包括注解类型)和枚举声明
@Target(ElementType.TYPE)
//注解信息将在运行时保留
@Retention(RetentionPolicy.RUNTIME)
//在使用 Javadoc 工具生成 API 文档时,该注解将被包含在文档中
@Documented
//如果一个类使用了@SpringBootApplication 注解,那么它的子类也会继承这个注解
@Inherited
//springboot配置类
@SpringBootConfiguration
//核心:启用Spring Boot 的自动配置机制
@EnableAutoConfiguration
//启用组件扫描
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
- 查看@EnableAutoConfiguration,@EnableAutoConfiguration注解上导入了AutoConfigurationImportSelector.class,该类负责选择和导入需要自动配置的类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//指定自动配置的包路径,通常用于扫描带有@Configuration 注解的类
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
- 查看AutoConfigurationImportSelector.class,该类负责加载、筛选和返回需要加载的自动配置类
public class AutoConfigurationImportSelector implements ... {
// 核心方法:选择并加载自动配置类
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1. 检查是否启用自动配置
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS; // 如果未启用,返回空数组
}
// 2. 获取自动配置条目
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
// 3. 返回配置类数组
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
- 查看selectImports中调用的getAutoConfigurationEntry方法。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 1. 检查是否启用自动配置
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY; // 如果未启用,返回空条目
}
// 2. 获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3. 加载候选配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 4. 去重
configurations = removeDuplicates(configurations);
// 5. 获取排除项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 6. 检查排除项
checkExcludedClasses(configurations, exclusions);
// 7. 过滤排除项
configurations.removeAll(exclusions);
// 8. 应用配置类过滤器
configurations = getConfigurationClassFilter().filter(configurations);
// 9. 触发事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 10. 返回结果
return new AutoConfigurationEntry(configurations, exclusions);
}
- 查看getAutoConfigurationEntry方法调用的getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 1. 加载候选配置类
ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, getBeanClassLoader());
List<String> configurations = importCandidates.getCandidates();
// 2. 检查配置类是否为空
Assert.notEmpty(configurations,
"No auto configuration classes found in " + "META-INF/spring/"
+ this.autoConfigurationAnnotation.getName() + ".imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
// 3. 返回配置类列表
return configurations;
}
- 查看getCandidateConfigurations调用的load方法,可以看到该方法负责从类路径中加载指定注解对应的 .imports 文件,并解析文件内容。
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
// 1. 检查注解是否为空
Assert.notNull(annotation, "'annotation' must not be null");
// 2. 决定类加载器
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// 3. 构建文件路径
String location = String.format("META-INF/spring/%s.imports", annotation.getName());
// 4. 查找文件 URL
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
// 5. 读取文件内容
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
// 6. 返回结果
return new ImportCandidates(importCandidates);
}
- 最后我们终于知道,@SpringBootApplication通过层层修饰,最后到了load方法,load方法扫描并读取类路径下的.imports文件中的配置类。
1.2 通过条件注解注册配置类
我们选择mybatis的包来介绍如何注册的
-
找到mybatis的自动配置包
-
查看.imports文件
-
查看部分配置类:这个类的含义是在满足以下条件时,自动配置并注册一个 FreeMarkerLanguageDriver Bean:
a、 类路径中存在 FreeMarkerLanguageDriver 和 FreeMarkerLanguageDriverConfig 类。
b、 Spring 容器中尚未注册 FreeMarkerLanguageDriver 类型的 Bean。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class })
public static class FreeMarkerConfiguration {
@Bean
@ConditionalOnMissingBean
FreeMarkerLanguageDriver freeMarkerLanguageDriver(FreeMarkerLanguageDriverConfig config) {
return new FreeMarkerLanguageDriver(config);
}
2 案例:自定义一个自动配置类
- 需求:写一个自动配置类,当访问http://localhost:8080/sayhello时,使用控制器使用自动配置类去打印“HELLO!”
- 创建业务类
public class GreetingService {
private String message = "Hello!";
public String sayHello() {
return message;
}
// 支持自定义问候语
public void setMessage(String message) {
this.message = message;
}
}
- 创建业务自动配置类
@Configuration
public class GreetingServiceAutoConfiguration {
@Bean
public GreetingService greetingService() {
return new GreetingService();
}
}
-
注册配置
-
编写控制器
@RestController
public class HelloController {
@Autowired private GreetingService service;
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
@RequestMapping("/sayhello")
public String sayHello(){
return service.sayHello();
}
}
- 项目结构
- 效果图