处理@Import分为3种:
1、ImportSelector:这种方式是返回一个字符串,由给定的字符串让spring去new一个类。然后动态添加BeanDefinition,这new这个过程无法参与。
2、ImportBeanDefinitionRegistrar:这种方式是将注册器给程序员,这样程序员就能动态的改变BeanDefinition,比前者权限更大。
3、普通类:这个没有什么特别的,这种方式不多赘述,跟ImportSelector比较相似。
我们直接到关键代码:
org.springframework.context.annotation.ConfigurationClassParser#processImports
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)) {//ImportSelector类型
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {//Deferred是延迟,表示后续处理
this.deferredImportSelectorHandler.handle(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)) {//ImportBeanDefinitionRegistrar类型
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
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
/**
* 否则,加入到importStack后调用processConfigurationClass 进行处理,processConfigurationClass里面主要就是把类放到configurationClasses
* configurationClasses是一个集合,会在后面拿出来解析成bd继而注册,可以看到普通类在扫描出来的时候就被注册了
* 如果是importSelector,会先放到configurationClasses后面进行出来注册
*/
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();
}
}
}
这里我们写3个测试类讲解:
package com.test.MYImport;
public class MYcommon {
}
package com.test.MYImport;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MYImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
package com.test.MYImport;
import com.test.dao.IndexDao3;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MYImportSeletor implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{IndexDao3.class.getName()};
}
}
这3个就是3种import方式。至于@Import添加就自己去添加。
我们来看第一种情况ImportSelector:
当进入这个判断之后就会拿到ImportSelector方法给的字符串,由这个字符串找到这个类,然后执行processImports方法递归调用,由于拿到的IndexDao3这个类是一个普通类,所以就放到importStack中,然后调用processConfigurationClass方法,这个方法最终将这个类存到configurationClasses这个map中,方便后面注册。
第二种情况我们看ImportBeanDefinitionRegistrar方法:
最终调用configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());这个方法做的事情就是把它put到importBeanDefinitionRegistrars这个map中,方便后面注册。
那么是在什么时候注册的呢?通过调试发现是在org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions中的this.reader.loadBeanDefinitions(configClasses);这行代码中,截图如下
从这里能够看出注册是这里注册的,那么这行代码做了什么呢?
/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {//注册selector
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//xml
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//注册Registrar
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
注册selector这个方法就是spring内部根据规则设置了属性,程序员没法插手。
注册Registrar这个方法就是能够获得registry注册器,执行org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
通过这个调用我们自己实现了ImportBeanDefinitionRegistrar方法的方式,因为这个方法可以得到BeanDefinitionregistry,所以能够动态添加BeanDefinition,也能够改变BeanDefinition,mybatis的mapperscan就是经典的应用场景。