文章目录
一、简介
本文将对 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 content
if (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 classes
if (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 first
processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
for (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 annotations
Set<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 immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (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 annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes 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 methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (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 recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return 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 imports
Class<?> 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 definitions
Class<?> 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 class
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();
}
}
}
可以看到核心代码就是对 @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
要延迟到最后处理,而且会根据不同的顺序和分组,分批处理。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/154471.html