前言
本文重点研究@Import注解的使用与源码解析
从 SpringIOC源码:@Configuration配置类解析过程一文中,Spring通过一系列操作后,会使用ConfigurationClassParser
类的processImports
方法解析@Import
,本文源码讲解就从这里开始
一、@Import的使用
说明:只能标注在类上,属性是
Class<?>[] value()
1、注册普通bean
@Import(Person.class)
@Configuration
public class App{}
public class Person {
}
其别名是“包名.类名”
2、注册配置类
@Import({TestConfig.class)
@Configuration
public class AppConfig {
}
@Configuration
public class TestConfig {
@Bean
public Person person(){
return new Person();
}
}
注意:这时候person的别名不是“包名.类名”,是和普通的一样,而TestConfig的别名是“包名.类名”
注意:如果主配置类AppConfig中如下
@Configuration
@Import({TestConfig.class)
public class AppConfig {
@Bean
public Person person(){
return new Person();
}
}
TestConfig中的person方法不会执行,而AppConfig中的会,除非AppConfig的没有
3、注册ImportSelector的实现类
ImportSelector是Spring中导入外部配置的核心接口
public class MyImport implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Color.class.getName()}; }
}
@Configuration
@Import(MyImport.class)
public class CarFactory { }
导入的Color的别名是cn.dao.Color 别名是“包名.类名”,
4、注册ImportBeanDefinitionRegistrar的实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean sign = registry.containsBeanDefinition("car”);
if(!sign){
registry.registerBeanDefinition("color", new RootBeanDefinition(Color.class));
}
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class CarFactory {
}
二、源码解析
1、一阶段解析
class ConfigurationClassParser {
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//根据{@code@Conditional}批注确定是否应跳过项。
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//忽略代码.........
// 递归地处理配置类及其超类层次结构。
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//最终把解析到的类组装成ConfigurationClass形式放到缓存configurationClasses中
this.configurationClasses.put(configClass, configClass);
}
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
//处理@Component注解
//处理@PropertySource注解
//处理@ComponentScan注解
// 处理任何@Import注释
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 处理任何@ImportResource注释
// 处理单个@Bean方法
// 处理接口上的默认方法
}
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 {
//循环@Import注解要value的值
for (SourceClass candidate : importCandidates) {
//如果是ImportSelector类型
if (candidate.isAssignable(ImportSelector.class)) {
//获取class
Class<?> candidateClass = candidate.loadClass();
//实例当前ImportSelector对象
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//如果当前selector实现了Aware接口,那么执行其具体的内部回调接口
ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);
//如果value的值是DeferredImportSelector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}//如果value的值不是DeferredImportSelector
else {
//执行其selectImports方法。获取要注册的springbean全类名
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
//类型转换,可以忽略
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
//递归调用当前方法,处理要注册的类
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
//如果value是ImportBeanDefinitionRegistrar类型
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);
//把当前ImportBeanDefinitionRegistrar对象缓存起来
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 候选类不是ImportSelector或ImportBeanDefinitionRegistrar
// 将其作为@Configuration类处理
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//processConfigurationClass的底层是doProcessConfigurationClass方法
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();
}
}
}
1、获取@Import注解value值的对象。
2、判断他们的类型
3、如果是ImportSelector类型:再判断其是否是DeferredImportSelector类型;如果是把其收集起来,等到一阶段解析完成后在进行解析。如果不是,执行其selectImports方法,获取要注册的springBean信息。再次判断他们的类型直至他们是普通javaBean类型
4、如果是ImportBeanDefinitionRegistrar类型:把他放到缓存中,以便二阶段解析注册。
5、如果是普通javaBean类型:把他作为配置类,解析一阶段解析,直至其没有被@Configuration,@Bean,@Component,@ComponentScan,@Import,@ImportResource注解标注。这时候把他放到缓存configurationClasses中。以便二阶段解析注册。
普通javaBean类型:在这里的意思是候选类不是ImportSelector或ImportBeanDefinitionRegistrar
2、二阶段解析注册
#ConfigurationClassPostProcessor
//configClasses是一阶段缓存进去要注册的类,ImportSelector,ImportBeanDefinitionRegistrar
this.reader.loadBeanDefinitions(configClasses);
在这里我们只分析@Import注解部分
class ConfigurationClassBeanDefinitionReader {
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//忽略代码。。。。。。。
//如果其是通过@Import注解注册过来的普通javabean,ImportBeanDefinitionRegistrar类型不会进来
if (configClass.isImported()) {
//注册其BeanDefinition到容器Spring中
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//....注册通过@bean注解标注的类
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//解析ImportBeanDefinitionRegistrar类型,执行其registerBeanDefinitions方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
三、疑问
1、ImportSelector,ImportBeanDefinitionRegistrar以及DeferredImportSelector
在上面的processImports方法中已经讲解了对所有的@Import注解中value值为不同指的情况进行解析。有以下的情况:
1、ImportSelector接口的实现类
2、DeferredImportSelector接口的实现类
3、ImportBeanDefinitionRegistrar接口的实现类
4、非以上3中接口的实现类,也就是普通的类
这里主要讲解1到3这三个接口类的区别
ImportSelector
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
ImportSelector接口作用将方法返回的字符串数组作为bean注入到容器中,注意这里的字符串需要是对象的全路径名称比如A.class.getName()这种。
ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
ImportBeanDefinitionRegistrar这个接口作用是,用户可以实现了之后来自定义来注册需要注册的bean
。可以设置自定义的BeanNameGeneratorbean名称生成规则。
DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector {
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
......
}
- DeferredImportSelector是ImportSelector的一个扩展;
- ImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理之前,所谓的其他逻辑,包括对@ImportResource、@Bean这些注解的处理(注意,这里只是对@Bean修饰的方法的处理,并不是立即调用@Bean修饰的方法,这个区别很重要!);
- DeferredImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理完毕之后,所谓的其他逻辑,包括对@ImportResource、@Bean这些注解的处理;
- DeferredImportSelector的实现类可以用Order注解,或者实现Ordered接口来对selectImports的执行顺序排序;