一 介绍
既然讲解到了spring关于@Configuration的解析,那就不得不讲一下SpringBoot中的自动装配的原理了。
SpringBoot的自动装配的原理不复杂,实际上就是使用@Import注解,注入一个ImportSelector类型的导入外部配置的核心类AutoConfigurationImportSelector,然后调用ImportSelector中的selectImports(AnnotationMetadata annotationMetadata) 方法[这个实际上各个版本的处理不一样,下面源码分析会讲到],借助于SpringFactoriesLoader的loadFactoryNames方法读取jar包中的META-INF/spring.factories文件,然后匹配符合的类型装配到spring中的容器中
二 源码分析
话不多说,带着过程我们在一点一点的分析Spring Boot的源码实现
首先,SpringBoot中的主函数中,会引入 @SpringBootApplication 类型的注解类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
..........
}
SpringBootApplication注解类中,会引入三个主要的注解 @SpringBootConfiguratioin,@EnableAutoConfiguratioin,@ComponentScan,其中,第一个注解@SpringBootConfiguratioin实际上就是一个@Configuration注解,表明spring boot启动的时候会默认的引导到当前的主函数类视为一个@Configuration类进行解析(前一篇文档讲过)
有一个细节,我们需要关注,SpringBoot在注入ConfigurationClassPostProcessor 解析@Configuration的BeanFactoryPostProcessor的时候,首先是创建ApplicationContext,其实在SpringBoot创建ApplicationContext的时候,是进行了系统环境的判断,来决定使用AnnotationConfigServletWebServerApplicationContext 还是AnnotationConfigReactiveWebServerApplicationContext 和 AnnotationConfigApplicationContext 这三个中的哪个,这一部分的源码是在SpringApplication#createApplicationContext() 方法中,可以自行研究
第二个注解@EnableAutoConfiguration就是SpringBoot自动装配的核心,@EnableAutoConfiguration注解中,会使用@Import注解注入一个 AutoConfigurationImportSelector 类,这个类是ImportSelector类,
在上一篇文档中Spring源码解读-@Configuration配置等注解源码分析,我们了解到,Spring使用ConfigurationClassPostProcessor进行处理@ImportSelector注解的时候,是会调用ImportSelector类的selectImports(AnnotationMetadata annotationMetadata) 方法,将引导入的装配类以字符串数组的形式返回,然后注入到ioc容器中(这个解析过程是在ConfigurationClassParser#processImports 方法)
/**
* @Import 的解析分三种情况
* value:
* 1,ImportSelector类型
* 2,ImportBeanDefinitionRegistrar
* 3,普通类
* @param configClass
* @param currentSourceClass
* @param importCandidates
* @param checkForCircularImports
*/
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
/**
* 判断@Import 的resource是否为Aware,加入对应的register environment对象,
*/
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
实际上,SpringBoot注入的AutoConfigurationImportSelector实现了DeferredImportSelector接口,在SpringBoot2.0版本之后,DeferredImportSelector 接口引入了一个Group接口,AutoConfigurationImportSelector 接口中实现了这个接口 AutoConfigurationGroup ,而实际的执行是调用到了 DeferredImportSelector 内部接口Group中的process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法, 这个方法在 AutoConfigurationImportSelector 的内部类AutoConfigurationGroup 有实现,这个流程有点复杂,大致的调用路线就是:
ConfigurationClassParser#processImports --> DeferredImportSelectorHandler#handle --> DeferredImportSelectorHandler#processGroupImports --> DeferredImportSelectorGrouping【这个实际就是AutoConfigurationImportSelector内部类AutoConfigurationGroup】#getImports–> AutoConfigurationGroup#process–> AutoConfigurationImportSelector#getAutoConfigurationEntry --> SpringFactoriesLoader#loadFactoryNames
因此,在SpringBoot2.1版本之后,你会发现如果你在AutoConfigurationImportSelector中的selectImports方法中打断点,它死活都不会进去,Spring版本的优化细节还是挺多的
最终,我们进入到了SpringFactoriesLoader中的loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) 方法
首先,先看一下这个方法传递的值是什么
第一个参数factoryClass传递的是通过getSpringFactoriesLoaderFactoryClass() 方法获取的
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
直接返回一个EnableAutoConfiguration的Class字节码对象
第二个参数就是返回当前ClassLoader,最终的解析过程是交由SpringFactoriesLoader的loadSpringFactories进行处理
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这段代码的核心思路就是通过ClassLoader的getResource()方法,从classpath跟目录下扫描指定目录META-INF/spring.factories下的配置文件,并解析文件内容,找到符合key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类装配到对应的spring ioc中
三 源码地址
源码地址 | 源码说明 |
---|---|
https://github.com/zcswl7961/spring | 基于Spring5 |