敞开生长之旅!这是我参加「日新方案 2 月更文应战」的第 1 天,点击检查活动概况

在项目开发的过程中,咱们会遇到许多名字为 @Enablexxx 的注解,比方@EnableApolloConfig@EnableFeignClients@EnableAsync 等。他们的功用都是经过这样的注解完成一个开关,决议了是否敞开某个功用模块的一切组件的自动化装备,这极大的降低了咱们的运用本钱。

那么你是猎奇过 @Enablexxx 是如何达到这种效果呢,其效果机制是怎么样的呢?

@Import 原理

依照默认的习气,咱们会把某个功用模块的敞开注解定义为 @Enablexxx,功用的完成和名字格式其实无关,而是其内部完成,这儿用 @EnableAsync 来举比方。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public@interfaceEnableAsync{
……
}

能够看到除了3个通用注解,还有一个@Import(AsyncConfigurationSelector.class)注解,明显它真正在这儿发挥了关键效果,它能够往容器中注入一个装备类。

在 Spring 容器启动的过程中,履行到调用invokeBeanFactoryPostProcessors(beanFactory)办法的时候,会调用一切现已注册的 BeanFactoryPostProcessor,然后会调用完成 BeanDefinitionRegistryPostProcessor 接口的后置处理器 ConfigurationClassPostProcessor ,调用其 postProcessBeanDefinitionRegistry() 办法, 在这儿会解析经过注解装备的类,然后调用 ConfigurationClassParser#doProcessConfigurationClass() 办法,最终会走到processImports()办法,对 @Import 注解进行处理,具体流程如下。

假如这部分流程不是很了解,引荐具体阅读一下 Spring 生命周期相关的代码,不过不重要,不影响了解后边的内容。

@Import注解原理

@Import 注解的功用是在ConfigurationClassParser类的 processImports()办法中完成的,关于这个办法我现已做了具体的注释,请检查。

privatevoidprocessImports(ConfigurationClassconfigClass,SourceClasscurrentSourceClass,
Collection<SourceClass>importCandidates,Predicate<String>exclusionFilter,
booleancheckForCircularImports){
//假如运用@Import注解润饰的类调集为空,直接回来
if(importCandidates.isEmpty()){
return;
}
//经过一个栈结构处理循环引进
if(checkForCircularImports&&isChainedImportOnStack(configClass)){
this.problemReporter.error(newCircularImportProblem(configClass,this.importStack));
}
else{
//添加到栈中,用于处理循环import的问题
this.importStack.push(configClass);
try{
//遍历每一个@Import注解的类
for(SourceClasscandidate:importCandidates){
//1.
//查验装备类Import引进的类是否是ImportSelector子类
if(candidate.isAssignable(ImportSelector.class)){
//CandidateclassisanImportSelector->delegatetoittodetermineimports
//候选类是一个导入选择器->托付来确定是否进行导入
Class<?>candidateClass=candidate.loadClass();
//经过反射生成一个ImportSelect目标
ImportSelectorselector=ParserStrategyUtils.instantiateClass(candidateClass,ImportSelector.class,
this.environment,this.resourceLoader,this.registry);
//获取选择器的额外过滤器
Predicate<String>selectorFilter=selector.getExclusionFilter();
if(selectorFilter!=null){
exclusionFilter=exclusionFilter.or(selectorFilter);
}

//判别引用选择器是否是DeferredImportSelector接口的实例
//假如是则运用选择器将会在一切的装备类都加载完毕后加载
if(selectorinstanceofDeferredImportSelector){
//将选择器添加到deferredImportSelectorHandler实例中,预留到一切的装备类加载完成后统一处理自动化装备类
this.deferredImportSelectorHandler.handle(configClass,(DeferredImportSelector)selector);
}

else{
//获取引进的类,然后运用递归方式将这些类中相同添加了@Import注解引用的类
//履行ImportSelector.selectImports
String[]importClassNames=selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass>importSourceClasses=asSourceClasses(importClassNames,exclusionFilter);
//递归处理,被Import进来的类也有或许@Import注解
processImports(configClass,currentSourceClass,importSourceClasses,exclusionFilter,false);
}
}
//2.
//假如是完成了ImportBeanDefinitionRegistrar接口的bd
elseif(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)){
//CandidateclassisanImportBeanDefinitionRegistrar->
//delegatetoittoregisteradditionalbeandefinitions
//候选类是ImportBeanDefinitionRegistrar->托付给当时注册器注册其他bean
Class<?>candidateClass=candidate.loadClass();
ImportBeanDefinitionRegistrarregistrar=
ParserStrategyUtils.instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class,
this.environment,this.resourceLoader,this.registry);
/**
*放到当时configClass的importBeanDefinitionRegistrars中
*在ConfigurationClassPostProcessor处理configClass时会随之一同处理
*/
configClass.addImportBeanDefinitionRegistrar(registrar,currentSourceClass.getMetadata());
}
else{
//CandidateclassnotanImportSelectororImportBeanDefinitionRegistrar->
//processitasan@Configurationclass
//候选类既不是ImportSelector也不是ImportBeanDefinitionRegistrar-->将其作为@Configuration装备类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(),candidate.getMetadata().getClassName());
/**
*假如Import的类型是一般类,则将其当作带有@Configuration的类一样处理
*/
processConfigurationClass(candidate.asConfigClass(configClass),exclusionFilter);
}
}
}
catch(BeanDefinitionStoreExceptionex){
……
finally{
this.importStack.pop();
}
}
}

上述代码的中心逻辑无非便是如下几个过程。

  1. 找到被 @Import 润饰的候选类调集,顺次循环遍历。
  2. 假如该类完成了ImportSelector接口,就调用 ImportSelectorselectImports() 办法,这个办法回来的是一批装备类的全限定名,然后递归调用processImports()持续解析这些装备类,比方能够 @Import 的类里面有 @Import 注解,在这儿能够递归处理。
  3. 假如被润饰的类没有完成 ImportSelector 接口,而是完成了ImportBeanDefinitionRegistrar 接口,则把对应的实例放入importBeanDefinitionRegistrars 这个Map中,等到ConfigurationClassPostProcessor处理 configClass 的时候,会与其他装备类一同被调用 ImportBeanDefinitionRegistrarregisterBeanDefinitions() 办法,以完成往 Spring 容器中注入一些 BeanDefinition。
  4. 假如以上的两个接口都未完成,则进入 else 逻辑,将其作为一般的 @Configuration 装备类进行解析。

所以到这儿,你应该明白 @Import 的效果机制了吧。对上述逻辑我总结了一张图,如下。

@Import注解原理

示例 @EnableAsync

持续之前提到的 @EnableAsync 作为比方,源码如下。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public@interfaceEnableAsync{
Class<?extendsAnnotation>annotation()defaultAnnotation.class;
booleanproxyTargetClass()defaultfalse;
AdviceModemode()defaultAdviceMode.PROXY;
intorder()defaultOrdered.LOWEST_PRECEDENCE;
}
//
@Override
publicfinalString[]selectImports(AnnotationMetadataimportingClassMetadata){
……
//获取Mode
AdviceModeadviceMode=attributes.getEnum(getAdviceModeAttributeName());
//模板办法,由子类去完成
String[]imports=selectImports(adviceMode);
if(imports==null){
thrownewIllegalArgumentException("UnknownAdviceMode:"+adviceMode);
}
returnimports;
}
publicclassAsyncConfigurationSelectorextendsAdviceModeImportSelector<EnableAsync>{
@Override
@Nullable
publicString[]selectImports(AdviceModeadviceMode){
switch(adviceMode){
casePROXY:
returnnewString[]{ProxyAsyncConfiguration.class.getName()};
caseASPECTJ:
returnnewString[]{ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
returnnull;
}
}
}

它经过 @Import 注解引进了AsyncConfigurationSelector装备类,它承继了 AdviceModeImportSelector 类,而后者完成了 ImportSelector 接口,里面的完成了一个由注解指定 mode 属性来决议回来的装备类的逻辑,而 mode 的默认值便是 AdviceMode.PROXY

@Import注解原理

对应 switch 逻辑,将回来 ProxyAsyncConfiguration类的全限定名。这就对应了 @Import 处理逻辑的第一个 if 逻辑块,它将会解析这个类,然后递归调用processImports(),再次进入此办法,进入第三个else逻辑块,将其当作一个一般装备类解析。能够看到 ProxyAsyncConfiguration 其实便是 @Configuration 类,它的效果是注册一个 Bean 目标 AsyncAnnotationBeanPostProcessor。

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicclassProxyAsyncConfigurationextendsAbstractAsyncConfiguration{
@Bean(name=TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
publicAsyncAnnotationBeanPostProcessorasyncAdvisor(){
……
returnbpp;
}
}