在学习Spring Boot中,最核心的两个原理就是 约定优于配置,自动装配,其实约定也是用于自动装配的,今天我们来分析一下,Spring Boot的自动装配是这么个过程。
@EnableAutoConfiguration 注解开启自动装配功能,这个注解是在启动类@SpringBootApplication 注解里面,所以当run 启动项目时就自动开启了spring boot 的自动装配功能
@Enable注解在spring 的作用就是把相关组件的Bean 装配到 Ioc 容器中。如果基于JavaConfig 的形式来完成Bean的装配的话就必须要使用 @Configuration 和@Bean注解,而@Enable 本质上就是针对这两个注解的封装,并且这些注解里面还携带了一个 @Import注解,答案就在@Import注解的配置类,用来描述自动装配的实现类
@Import注解的作用就是 Spring 会解析到 @Import导入到配置类,从而通过这个配置类的描述来完成Bean的装配
在进入@EnableAutoConfiguration 注解中,我们可以看到 不仅有 @Import 还有 @AutoConfigurationPackage 这个注解的作用就是:把使用了该注解的类的所在的包及子包下所有的组件扫描到Spring Ioc容器中。
AutoConfigurationImportSelector 类实现了 ImportSelector,中的selectImports 抽象方法,并且返回一个数组,这个方法的实现就是Spring Boot自动装配的实现,这里会把需要自动装配的类配置好,放到到这个数组,然后进行批量装配。
当前Spring Boot 版本为2.3.10.BUILD-SNAPSHOT 在低版本中,这里面应该还有一段代码是用处理:
1.META-INF/spring-autocomfigure-metadataproperties中加载自动装配的条件元数据,只有满足条件的Bean才能进行自动装配,在当前版本中,我们并没有看到这段处理代码,
2.收集所有符合条件的配置类,完成自动装配
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
这段代码中重点就在于getAutoConfigurationEntry方法中,这里是收集自动装配到配置类。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
分析一下代码:
读取 EnableAutoConfiguration 注解中的属性信息等 (需要排除的配置类)
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
这里是最重要的点,获取所有自动装配的配置类。
protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List 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;
}
这里使用了 SpringFactoriesLoader 约定俗成的加载方式,也点类似java中的SPI,简单来说就是通过 classpath 读取 META-INF/spring.factories,这个文件就存储了我们需要自动装配的配置类,文件以key-value方法存储
文件中就以这种形式存在:
里面的格式就这样,条件装配,这样子我们就可以把他自动装配进去。其实这里我理解就是 Spring boot Starter 约定的一些文件,然后读取出来进行装配。
去掉重复的配置类,里面的实现很简单,转换成 LinkedHashSet
protected final List removeDuplicates(List list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
根据EnableAutoConfiguration注解中属性,获取不需要自动装配的类名单
protected Set getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
exclude 不需要自动装配的类
excludeName 不需要自动装配类的名称
getExcludeAutoConfigurationsProperty 获取配置文件中不需要自动装配的类:
String ENABLED_OVERRIDE_PROPERTY = “spring.boot.enableautoconfiguration”;
取代不需要装配的配置类
进行过滤操作,不需要装配的类
自动装配的导入事件,事件可能包括候选的配置名单,和排除的配置名单
return new AutoConfigurationEntry(configurations, exclusions);
返回需要自动装配的类。
至此Spring Boot 自动装配原理分析完毕。