文章目录
- 一、简介
- 二、@Import注解的几种用法
- 三、@Import注解源码解析
- 1、ConfigurationClassPostProcessor
- 2、ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry 方法
- 3、ConfigurationClassParser 类的 parse 方法
- 4、处理 ImportBeanDefinitionRegistrar 接口实现类
- 5、处理 ImportSelector 接口实现类
- 6、处理 DeferredImportSelector 接口实现类
一、简介
本文将对 Spring 如何处理 @Import
注解的源码进行解析
二、@Import注解的几种用法
在@Import注解的参数里可以填写一个类的数组,而源码上已经告诉我们常用的几种导入类型
- 导入的类是一个标注了
@Configuration
的配置类:Spring容器实例化这个配置类 - 导入的类是
ImportSelector
接口的实现类:如果导入的类是ImportSelector
接口的实现类,实例化这个类之后,会执行其selectImports
方法,如果实现的是ImportSelector
接口的子类DeferredImportSelector
,会执行其selectImports
方法,但是时机会延后,这个后面具体讲 - 导入的类是
ImportBeanDefinitionRegistrar
接口的实现类:如果导入的类是ImportBeanDefinitionRegistrar
接口的实现类,实例化这个类之后,会执行其registerBeanDefinitions
方法
三、@Import注解源码解析
1、ConfigurationClassPostProcessor
下面通过源码来看 Spring 是怎么处理 @Import 注解的,关于@Import注解的处理过程,在 ConfigurationClassPostProcessor
这个类里,这个类是用来处理 @Configuration
注解相关逻辑的,它实现了 BeanDefinitionRegistryPostProcessor
接口,而 BeanDefinitionRegistryPostProcessor
接口又是 BeanFactoryPostProcessor
的子接口,熟悉 Spring 源码的同学会比较熟悉这两个类,这是Spring的后置处理器
BeanFactoryPostProcessor
:用于修改Bean定义(BeanDefinition
)BeanDefinitionRegistryPostProcessor
:用来注册BeanDefinition
到Spring容器里
具体这里就不展开了,想了解的可以去看看 Spring 的核心方法 refresh
方法里的 invokeBeanFactoryPostProcessors(beanFactory);
,这里只需要知道,在 Spring 容器启动过程中,会调用 ConfigurationClassPostProcessor
类的 postProcessBeanDefinitionRegistry
方法,下面我们从这个方法的入口开始看
2、ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry 方法
前面那一段,是一个防重复处理的逻辑,不用关心,重点是 processConfigBeanDefinitions
方法,跟进去看下
//ConfigurationClassPostProcessor
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {//1、找到所有的配置类List<BeanDefinitionHolder> configCandidates = new ArrayList<>();String[] candidateNames = registry.getBeanDefinitionNames();for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// Return immediately if no @Configuration classes were found//2、如果没找到配置类就直接返回if (configCandidates.isEmpty()) {return;}// Sort by previously determined @Order value, if applicable//3、为找到的配置类排序configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// Detect any custom bean name generation strategy supplied through the enclosing application context//4、如果registry是SingletonBeanRegistry类型,且包含这个CONFIGURATION_BEAN_NAME_GENERATOR实例,就设置两个属性SingletonBeanRegistry sbr = null;if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;if (!this.localBeanNameGeneratorSet) {BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);if (generator != null) {this.componentScanBeanNameGenerator = generator;this.importBeanNameGenerator = generator;}}}if (this.environment == null) {this.environment = new StandardEnvironment();}// Parse each @Configuration class//5、实例化ConfigurationClassParser对象ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());do {//6、处理配置类parser.parse(candidates);//7、验证配置类的合法性和正确性parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}//加载Bean定义到Spring容器里this.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classesif (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {// Clear cache in externally provided MetadataReaderFactory; this is a no-op// for a shared cache since it'll be cleared by the ApplicationContext.((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}
}
这个方法可以简单的概括成以下几个步骤:
- 找到所有的配置类;
- 循环解析每个配置类;
- 把配置类加载到 Spring 容器里;
最重要的就是解析配置类,ConfigurationClassParser
类的 parse
方法,我们跟进去看下
3、ConfigurationClassParser 类的 parse 方法
//ConfigurationClassParser
public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else {parse(bd.getBeanClassName(), holder.getBeanName());}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);}}this.deferredImportSelectorHandler.process();
}
不论是哪个 if 条件,最终都会走到 ConfigurationClassParser
类的 processConfigurationClass
方法里,而且这些方法会将 DeferredImportSelector
接口的实现类放到 deferredImportSelectorHandler
里,在这个方法最后,调用这些类
这里先跳过,我们先看 ConfigurationClassParser
类的 processConfigurationClass
方法
//ConfigurationClassParser
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}// Otherwise ignore new imported config class; existing non-imported class overrides it.return;}else {// Explicit bean definition found, probably replacing an import.// Let's remove the old one and go with the new one.this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// Recursively process the configuration class and its superclass hierarchy.SourceClass sourceClass = asSourceClass(configClass);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass);}while (sourceClass != null);this.configurationClasses.put(configClass, configClass);
}
重点是 doProcessConfigurationClass
方法,跟进
//ConfigurationClassParser
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass);}// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotationsSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotationsprocessImports(configClass, sourceClass, getImports(sourceClass), true);// Process any @ImportResource annotationsAnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;
}
这个方法也是很长的一段,关于 @Import
注解的其实就在 processImports
方法里,我们跟进看下
//ConfigurationClassParser
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 importsClass<?> candidateClass = candidate.loadClass();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 {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 definitionsClass<?> 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 classthis.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();}}
}
可以看到核心代码就是对 @Import
注解的参数,那个 Class 数组做循环处理,而 if 条件是按我们之前所说的三种情况做区分的
@Configuration
注解类型的就不讲了,就是再回去调用处理 @Configuration
注解的方法,下面重点看下 ImportBeanDefinitionRegistrar
接口和 ImportSelector
接口的场景
4、处理 ImportBeanDefinitionRegistrar 接口实现类
先是实例化这个类,如果这个类实现了 Aware
相关的接口,就去设置下相关的属性,最后调用 addImportBeanDefinitionRegistrar
方法,将对象放到 importBeanDefinitionRegistrars
这个 map 里
那么这个类的 registerBeanDefinitions
方法究竟在什么时候调用的呢?
上面我们讲到在解析完配置类之后,会调用 this.reader.loadBeanDefinitions(configClasses);
方法,我们跟进这个方法
继续跟进 loadBeanDefinitionsForConfigurationClass
方法
configClass.getImportBeanDefinitionRegistrars()
方法就是获取的上面所说的 importBeanDefinitionRegistrars
这个map,继续跟进 loadBeanDefinitionsFromRegistrars
方法
可以看到循环map,调用了 registerBeanDefinitions
方法
5、处理 ImportSelector 接口实现类
同样的,先实例化对象,然后处理 Aware
相关属性注入,然后开始区分了,如果是 DeferredImportSelector
接口的实现类,就封装成 DeferredImportSelectorHolder
对象,添加到 deferredImportSelectors
这个集合里,这个前面有说过,为了延迟调用,如果不是 DeferredImportSelector
接口的实现类,那就是 ImportSelector
接口的实现类,那么就先调用其 selectImports
方法,这个方法会返回 bean 的名字,然后转成 SourceClass
对象,再调用 processImports
方法处理,然后就看 selectImports
方法返回的 bean 是什么类型,再判断处理
总结一下,就是
- 如果实现了
DeferredImportSelector
接口,那么就存到deferredImportSelectors
集合里,等待后续处理 - 如果没有实现
DeferredImportSelector
接口,那么是实现了ImportSelector
接口,直接调用其selectImports
方法
所以,实现了 DeferredImportSelector
接口的类的 selectImports
方法,在什么时候调用的呢?
6、处理 DeferredImportSelector 接口实现类
我们回到 ConfigurationClassParser
类的 parse
方法
可以看到最后一步,就是处理所有 DeferredImportSelector
接口的实现类(后面简称deferredImports
)的方法,我们跟进去
这个方法里,先把所有的 deferredImports
排序,然后调用 DeferredImportSelectorGroupingHandler
的 register
方法,把这些类进行分组,最后再调用 processGroupImports
方法处理,跟进这个方法
可以看到对分组后的map,获取其内容,然后进行循环,获取每个分组中的 deferredImports
循环,调用 processImports
方法,我们看下 grouping.getImports()
返回的是什么
可以看到,正是调用 selectImports
方法的返回,所以处理 DeferredImportSelector
接口的实现类,和处理 ImportSelector
接口的实现类,大致是差不多的,只不过 DeferredImportSelector
要延迟到最后处理,而且会根据不同的顺序和分组,分批处理。
dler的
register方法,把这些类进行分组,最后再调用
processGroupImports` 方法处理,跟进这个方法
可以看到对分组后的map,获取其内容,然后进行循环,获取每个分组中的 deferredImports
循环,调用 processImports
方法,我们看下 grouping.getImports()
返回的是什么
可以看到,正是调用 selectImports
方法的返回,所以处理 DeferredImportSelector
接口的实现类,和处理 ImportSelector
接口的实现类,大致是差不多的,只不过 DeferredImportSelector
要延迟到最后处理,而且会根据不同的顺序和分组,分批处理。