试想一下,我们导入一些外部配置文件后,想要将这些配置文件引入到工程中使用,我们需要进行很多配置工作或者书写很多配置文件才能完成,废不废劲?麻不麻烦?
福音来到:Springboot的自动装配原理设计,很好的解决了这个问题,即Spring容器启动过后,一些自动装配类就会自动装配到IOC容器中,不需要我们手动注入,好开心!!!;
二、自动装配原理
1. SPI:是server provider interface 的简写,字面意思是服务发现接口,我理解就是第三方拓展API,他是java提出的,即开箱即用的思想。在spring的生态中已经运用得非常多,比如springMVC中官方推荐的去web.xml的方式,用一个onStart的方法就将dispatcherServlet到IOC的容器中,这也是利用了tomcat的SPI。在springBoot中,当我们加一个starter的包,spring将帮我们注入了一些类。你删掉包后,spring就不会注入。不会产生任何的影响,这就是SPI的思想,拆箱即用;
2. 分析注解
首先看一下流程图:
从启动类出发:
@SpringBootApplication
public class Springboot04DataApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot04DataApplication.class, args);
}
}
@SpringBootApplication 为组合注解,点击进入会发现由以下三个注解组成:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
自动装配的秘密在 @EnableAutoConfiguration 注解,点击进入会发现由以下两注解组成:
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)
首先分析@AutoConfigurationPackage
该注解字面分析为自动配置包,也为组合注解,点击进入发现主要由以下注解组成:
@Import(AutoConfigurationPackages.Registrar.class)
@Import注解导入了导⼊了⼀个Registrar 的组件,这个注解的作用就是将主配置类(标注了@SpringBootAutoConfiguration的类)所在的包及子包里面的组件扫描到IOC容器中。所以默认情况下主配置类所在的包及其子包的组件会被自动加载到IOC容器中;
其次再分析@Import(AutoConfigurationImportSelector.class)
通过@Import注解导入AutoConfigurationImportSelector类,该类中selectimport方法会通过SpingFactoriesLoader得到大量的配置类,最后通过每个配置类自身的条件做出决策(是否生效),完成配置类的自动加载;
重点看一下selectimport方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
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);
}
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;
}
主要做了三件事情:
1. 从classpath下获取META-INF/spring.factories这个文件下的信息;
2. 将上面获取的信息封装成Enumeration返回;
3. 遍历Enumeration,然后获取key为EnableAutoConfiguration下的所有值;
三、总结整个过程:
位于META-INF目录下的spring.factortes文件存放相关组件的配置工厂类;@EnableAutoConfiguration核心配置注解里面有一个AutoConfigurationImportSelector(自动配置文件收集器),收集配置文件中的配置工厂类,然后SpringFactoriesLoader加载组件工程(抽象工厂模式)进行实例化,在spring上下文中生成bean,完成自动配置功能。



