[!NOTE]
**Spring版本:**5.3.27
**JDK版本:**1.8
一、@Import在何处处理
// ConfigurationClassParser
/**
* 通过从源类(理解为配置类)中读取注解、成员和方法,获取到完成的配置类
* 由于一个配置类可能关联其他的配置类等,所以这个方法可能会被调用多次
*/
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 先递归处理成员类
processMemberClasses(configClass, sourceClass, filter);
}
// 处理@PropertySource注解
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");
}
}
// 处理@ComponentScan注解
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) {
// 配置类有@ComponentScan注解 -> 立即执行扫描
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 如果找到其他的配置类,在需要的情况下递归解析
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());
}
}
}
}
// 处理@Import注解 本文要探讨的注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 处理@ImportResource注解
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);
}
}
// 处理@Bean方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 处理接口上的默认方法
processInterfaces(configClass, sourceClass);
// 超类处理
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// 发现超类, 返回元注解并递归
return sourceClass.getSuperClass();
}
}
// 没有超类 -> 处理完成
return null;
}
// 获取通过@Import导入的类
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
/**
* 递归收集通过@Import导入的类
* 检查每个源类上的注解,如果不是@Import,把这个注解当成源类,继续收集
* 最终的结果就是获取到所有源类上所有层次@Import上的value
*/
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
/**
* importCandidates是上一步收集到的所有@Import注解的value
*/
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 省略部分代码...
// 遍历处理所有@Import注解的value
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 候选类是ImportSelector -> 委托给ImportSelector来确定导入的类
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 候选类是ImportBeanDefinitionRegistrar ->
// 委托给ImportBeanDefinitionRegistrar注册额外的bean定义信息
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 候选类不是ImportSelector或ImportBeanDefinitionRegistrar ->
// 当成配置类处理(@Configuration注解的类)
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
二、@Import引入的三种类型
1、引入普通组件
package com.lazy.snail;
/**
* @ClassName NormalComponent
* @Description TODO
* @Author lazysnail
* @Date 2024/10/22 9:35
* @Version 1.0
*/
public class NormalComponent {
}
package com.lazy.snail;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({NormalComponent.class})
public class AppConfig {
}
2、引入ImportSelector
package com.lazy.snail;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync
public class AppConfig {
}
// EnableAsync
// 省略部分代码...
/**
* 启用Spring的异步方法执行能力
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// AsyncConfigurationSelector会被选择出来进行处理
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
// ConfigurationClassParser.processImports截取
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 实例化了AsyncConfigurationSelector
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 这种类型后续SpringBoot自动装配再聊
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 此处的selectImports调用的是AsyncConfigurationSelector父类AdviceModeImportSelector的selectImports
// 各种选择逻辑后返回org.springframework.scheduling.annotation.ProxyAsyncConfiguration
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 根据全限定名找源Class
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 把上面找到的源类当做@Import引入的类,从走取经路
// 就本案例而言,ProxyAsyncConfiguration是普通组件,只有一个@Bean方法,所以会在容器中注册一个AsyncAnnotationBeanPostProcessor
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// AdviceModeImportSelector
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 省略部分代码...
// AsyncConfigurationSelector的selectImports方法
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
// AsyncConfigurationSelector
/**
* 这个方法返回了一个类的全限定名
* org.springframework.scheduling.annotation.ProxyAsyncConfiguration
*/
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
AsyncConfigurationSelector类结构图:
解析结束后,容器中的beanDefinitionMap:
3、引入ImportBeanDefinitionRegistrar
package com.lazy.snail;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 此处导入了ImportBeanDefinitionRegistrar类型的注册器
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
// ConfigurationClassParser.processImports
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
// AspectJAutoProxyRegistrar实例
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 注册器加到了配置类importBeanDefinitionRegistrars中,这个集合中的注册器会在加载bean定义信息的时候使用
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// ConfigurationClassPostProcessor.processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 省略部分代码...
this.reader.loadBeanDefinitions(configClasses);
// 省略部分代码...
}
// ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 省略部分代码...
// 此处处理之前添加的注册器
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
/**
* 遍历注册器,调用注册器的registerBeanDefinitions方法
*/
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
// AspectJAutoProxyRegistrar
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注册AspectJAnnotationAutoProxyCreator的bean定义信息
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
注册器处理完后,容器中的beanDefinitionMap中:
三、总结
1. 导入普通组件
使用方式:
@Import(SomeConfig.class)
处理机制:
-
当
@Import
注解的value
中传入的是普通的组件类(包括普通的 Java 类和标记了@Configuration
的类),Spring 容器会将这些类作为Bean直接注册到容器中。 -
解析顺序
:
- Spring 会将这些类当作标准的配置类进行处理(如果带有
@Configuration
)。 - 如果是普通类(没有
@Configuration
),也会作为普通的 Bean 定义注册进容器。
- Spring 会将这些类当作标准的配置类进行处理(如果带有
使用场景:
- 简单场景下,直接将某些普通组件或配置类引入容器进行自动装配和依赖注入。
2. 导入实现 ImportSelector
接口的类
使用方式:
@Import(MyImportSelector.class)
处理机制:
-
ImportSelector
是一个接口,其中最核心的方法是selectImports
,它返回一个字符串数组,表示要导入的类名。 -
解析顺序
:
- Spring 会调用
selectImports
方法,获取需要导入的 Bean 名称列表(这些类并不需要提前在@Import
注解中明确声明)。 - Spring 根据返回的类名,动态地将这些类的定义加载到容器中。
- 可以通过条件、配置、逻辑判断来选择哪些类需要被导入,实现更灵活的组件注册。
- Spring 会调用
使用场景:
- 需要基于逻辑条件动态引入配置类或组件,比如在某些场景下,按需引入特定 Bean。
- 常用于自动配置机制,例如 Spring Boot 的
@EnableAutoConfiguration
,其背后通过ImportSelector
实现了动态组件引入。
3. 导入实现 ImportBeanDefinitionRegistrar
接口的类
使用方式:
@Import(MyImportBeanDefinitionRegistrar.class)
处理机制:
-
ImportBeanDefinitionRegistrar
提供更底层的控制,它允许在 Spring Bean 定义的注册阶段,手动向容器中注册 Bean 定义信息。 -
解析顺序
:
- Spring 调用
registerBeanDefinitions
方法,该方法允许开发者直接向BeanDefinitionRegistry
中注册 Bean 定义,而不仅仅是类名。 - 在这个方法里,开发者可以完全自定义 Bean 的定义、依赖关系、属性等。
- 这是最强大的扩展方式,因为它可以直接操作 Bean 定义元数据,而不仅仅是控制导入哪些类。
- Spring 调用
使用场景:
- 需要完全自定义 Bean 注册过程,甚至可以动态生成
BeanDefinition
。 - 通常用于更复杂的场景,如框架级别的开发,动态注册特定类型的 Bean 或者与其他框架集成。
三者对比总结
形式 | 处理方式 | 使用场景 |
---|---|---|
普通组件(类) | 直接将类作为 Bean 注册到容器中 | 引入静态组件、配置类,或使用简单的 @Configuration 类 |
实现 ImportSelector 接口 | 通过返回类名字符串数组,动态选择和注册类 | 按条件动态选择要注册的 Bean,用于自动配置等场景 |
实现 ImportBeanDefinitionRegistrar 接口 | 手动操作 BeanDefinitionRegistry 注册 Bean 定义 | 完全自定义 Bean 的注册过程,实现更复杂的动态注册逻辑 |