自动装配作为SpringBoot四大核心(启动依赖、自动装配、Actuator、命令行界面)组件中最常用之一,重要性不言而喻。因涉及内容比较多,因此准备通过上下两节深入源码探究竟底层如何实现自动装配,通过阅读本篇以及下一篇可以清楚下面三个问题:
- 如何实现自动?
- 装配什么?
- 如何实现自定义starter?
因SpringBoot构建在Spring框架上,因此有必要先重点分析Spring中@Import注解,因此本节整体安排如下:
- @Import注解
- demo代码
- 源码分析
- 总结
本节重点解读@Import注解以及处理流程。
1、@Import
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration},
* {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar},
or regular component classes to import.
*/
Class<?>[] value();
}
从注解的注释可以看到@Import导入的类型可以分成以下三种;
类型 | 备注 |
普通类 | 将@Import导入的类定义注册到beanDefinitionMap |
ImportSelector | 将selectImports方法返回类所对应的类定义注册到beanDefinitionMap |
ImportBeanDefinitionRegistrar | 手动生成beanDefinition注册到容器beanDefinitionMap |
2、demo
- 三个需要导入的原始类
public class ImportBean {
}
public class ImportSelectBean {
}
public class RegistrarBean {
}
- ImportSelector 实现类
public class ImportSelectorImpl implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"xu.jiang.hua.im.ImportSelectBean"};
}
}
- ImportBeanDefinitionRegistrar实现类
public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//bean定义
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(RegistrarBean.class);
registry.registerBeanDefinition("registrarBean",bd);
}
}
- 导入配置类
//分别导入ImportBean
@Configuration
@Import({ImportBean.class, ImportSelectorImpl.class, ImportBeanDefinitionRegistrarImpl.class,})
public class Config {
}
3、源码分析
- doProcessConfigurationClass
接下来重点看看spring如何处理@Import注解,
.......
processImports(configClass, sourceClass, getImports(sourceClass), true);
.....
比如@EnableAutoConfiguration作为复合注解,里面包含@Import(AutoConfigurationImportSelector.class),因此getImports需要通过递归的方式获取到当前类注解上所导入的所有类,具体代码如下:
- getImports
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();用于记录已经访问过的sourceClass
collectImports(sourceClass, imports, visited);//通过sourceClass导入的所有类
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited){
if (visited.add(sourceClass)) {
//找出当前类上的所有注解,然后遍历注解。对每个注解进行递归,直到无注解或者是@Import for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
- processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
//省略非关键代码
}
else {
this.importStack.push(configClass);
try {
//遍历刚才找出的import类
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//实例化selector
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
//回调selector方法
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
//导入类上可能还存在@Import注解 因此继续递归
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);
//将导入注册类实例化后并调用各种aware接口,并注册到importBeanDefinitionRegistrars
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());
//生成新的configurationClass,并标记是由哪个类导入的
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();
}
}
}
整体流程如下图所示
- 判断导入类是否实现ImportSelector。如果是,则跳转第2步,否则跳转第4步;
- 实例化ImportSelector,继续判断是否实现DeferredImportSelector(DeferredImportSelector继承ImportSelector接口)。如果是,则加入DeferredImportSelectorHandler中的deferredImportSelectors中(此处与自动装配有关!!)。如果不是,则跳转第3步;
- 回调ImportSelector类的selectImports,得到需要导入的类,将此作为导入类继续调用processImports。
- 判断导入类是否实现ImportBeanDefinitionRegistrar,如果是则实例化,然后注册到 importBeanDefinitionRegistrars(key:ImportBeanDefinitionRegistrar,value:AnnotationMetadata)。如果不是,则跳转第5步;
- 将导入类生成ConfigurationClass类,并标记当前导入类由configClass导入,调用processConfigurationClass;
- 执行完doProcessConfigurationClass后configurationClasses
从上图中方框1、方框2、方框3和方框4可以看到ImportBean、ImportSelectBean通过ConfigurationClass表示并通过importBy属性表示被哪个类(此处为Config)导入,但此时还未将导入的类注册到beanDefinitionMap,接下来执行loadBeanDefinitions回调方法将类定义注册到beanDefinitionMap,上面也可以下图进行表示:
- loadBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//省略非核心关键代码
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//处理@Import标注的类以及方法上有@Bean标记的类(前面只是将相应的类找出来,并未注册到beanDefinitionMap中)
this.reader.loadBeanDefinitions(configClasses);
//省略非核心关键代码
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
//遍历所有的Configurations
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
接下来看loadBeanDefinitionsForConfigurationClass方法:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator){
//省略非关键代码
//如果类通过import导入,此时实现注册到beanDefinitionMap,比如ImportBean ImportSelectBean
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//将@Bean标记的方法注册到beanDefinitionMap
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//ImportResources
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//回调BeanDefinitionRegistrar的实现类,比如RegistrarBean就通过此处实现注入
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
可以看到loadBeanDefinitions不仅处理@Import导入的类,同时处理@Bean和@ImportResources注解,此处重点关注@Import的处理。
- registerBeanDefinitionForImportedConfigurationClass
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
//解析是否生成代理类,打标操作
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
//生成beanName
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
//解析该类的 @Lazy、@Primary、@DependsOn、@Role等属性 并设置到beanDefinition中
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
//生成代理类的definitionHolder
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//注册到beanDefinitionMap
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
//回设beanName
configClass.setBeanName(configBeanName);
if (logger.isTraceEnabled()) {
logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
}
}
通过传进来的configClass生成BeanDefinitionHolder通过this.registry.registerBeanDefinition方法手动注册到beanDefinitionMap中,接下来看看BeanDefinitionRegistrar回调处理
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
4、总结
通过简单demo对@Import完成分析,总结如下:
- 针对通过ImportSelect导入的类以及普通类,执行完processConfigurationClass后,会根据导入类生成ConfigurationClass放入private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>(),但是都还没注册beanDefinitionMap中,通过后续的this.reader.loadBeanDefinitions方法实现注册到 beanDefinitionMap;
- 通过ImportBeanDefinitionRegistrar接口手动注册的类,未放入configurationClasses中,将接口实现类标记在ConfigurationClass中的
importBeanDefinitionRegistrars属性上,最终通过this.reader.loadBeanDefinitions方法实现注册到
beanDefinitionMap;
通过本节对@Import注解的梳理,下节梳理自动装配将会变得容易。