Spring refresh - invokeBeanFactoryPostProcessors - 解析 @Import

博文目录


@Import 注解给 Spring bean 创建带来很大的灵活性,因其对配置的封装,极大简化了 Spring 的使用。Spring 中的 @Enable* 基本上都是通过 @Import 来实现的 (在 @Enable 上添加 @Import 注解来导入其他配置类啥的)

@Import 的使用方式

八、spring ioc之@Import解析

通常在配置类上使用 @Import 导入其他配置类

  • 有 @Configuration注解, 标记为 FULL 配置类
  • 没有 @Configuration 注解, 但是存在 @Component, @ComponentScan, @Import, @ImportResource, 或者类中有 @Bean 注解标注的方法, 标记为 LITE 配置类

@Import 的三种使用方式

  • 直接导入一个配置类 (4.2后也可以导入普通类)
  • 导入 ImportSelector 接口的实现类, 解析 selectImports 方法返回的一组类为 BeanDefinition 并注册
  • 导入 ImportBeanDefinitionRegistrar 接口的实现类, 自行构建并注册 BeanDefinition

哪些 @Import 能被解析

  • 配置类上的 @Import
  • 配置类上的其他注解内部的 @Import, 以及从配置类起, 递归非 @Import 注解内的 @Import

如果被扫描的 @Import 导入了一个非配置类, 但该类上有 @Import, 则也会被解析到

注意, 被 @Import 导入的类不会被注册成为 BeanDefinition, 不会生产单例 Bean

导入配置类

@EnableWebMvc

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {}

ImportSelector

@EnableAsync

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@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;
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    @Override
    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;
        }
    }
}

ImportSelector 的 selectImports 方法的主要作用是收集需要导入的配置类, 方法返回的一组类限定名会被当做配置类来解析(走解析配置类的完整流程, 会解析这个类上的 @PropertySource, @ComponentScan, @Import, @ImportSource, @Bean 方法 等), ImportSelector 本身并不会被注册成为 BeanDefinition

在调用该 ImportSelector 的 selectImports 方法之前, 如果该导入的 ImportSelector 还实现 EnvironmentAware, BeanFactoryAware, BeanClassLoaderAware, ResourceLoaderAware, 那么会将这些拿到并 set 到该 ImportSelector 的对应属性中

DeferredImportSelector 延迟导入

@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
	@Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), 
	@Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})}
)
public @interface SpringBootApplication {}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	// ...
    public Class<? extends Group> getImportGroup() {
        return AutoConfigurationImportSelector.AutoConfigurationGroup.class;
    }
	// ...
}

DeferredImportSelector 接口是 ImportSelector 接口的子接口,该接口引入的配置类将在Spring解析完其他注解和配置类(包括但不限于ImportSelector引入的配置类, 不包括自动化配置类即spring.factories文件中的配置类)后再解析, 依赖此设计可以实现SpringBoot的自动配置, 自动化配置类在被解析时会根据条件判断(@Conditional)是否Spring容器中已经存在用户配置的Bean, 存在的话就不会执行自动化配置了. 此接口还可以和接口Ordered或者@Order一起使用,定义多个DeferredImportSelector选择器的优先级

还提供分组功能, 先调用DeferredImportSelector的getImportGroup方法判断是否有自定义组, 有的话就调该组的selectImports方法, 没有的话就调DeferredImportSelector的selectImports方法. 不同的ImportSelector导入的Bean可以被分组, 在组的selectImports方法中可以排序

ImportBeanDefinitionRegistrar

@EnableAspectJAutoProxy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

ImportBeanDefinitionRegistrar 接口提供了 registerBeanDefinitions 方法的两个默认实现, 可以覆盖该方法, 自定义 BeanDefinition 并注册

解析 @Import 的流程

  • 从当前配置类起, 递归查找类上的 @Import 注解, 以及类上非 @Import 注解(注解类名以java开头的过滤掉)里面的 @Import 注解. 拿到所有 @Import 注解导入的类. 比如当前解析的是配置类, 配置类 上 @Import(A.class), A 上 @Import(B.class), 配置类 上还有一个 @Xxxxx 注解, 这个注解里 @Import(C.class), 最终拿到的是 A.class, C.class
  • 遍历拿到的被导入的类
    • 如果被导入的类 是 ImportSelector (导入型选择器: 用于选择需要导入的类)
      • 如果被导入的类 是延迟导入 DeferredImportSelector (延迟导入型选择器: 用于选择需要导入的类, 被导入的类的解析会延迟), 会把该导入型选择器存在 ConfigurationClassParser 的 deferredImportSelectors 中. 后面哪里处理
      • 如果被导入的类 不是延迟导入, 递归导入该类导入的类再导入的类. 递归解析的最后一定会到 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 的类
    • 如果被导入的类 是 ImportBeanDefinitionRegistrar (导入型 BeanDefinition 注册器: 用于注册需要导入的 BeanDefinition), 将这个 ImportBeanDefinitionRegistrar 添加到 configClass 的 importBeanDefinitionRegistrars 中. 在 parse 后面的 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 流程中被遍历调用其方法来注册 BeanDefinition
    • 如果被导入的类 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar, 将其作为一个配置类来解析, 走解析 @Configuration 相同的流程. 并没有直接生成 BeanDefinition 并注册, 而是先保存在 ConfigurationClassParser 的 importStack 的 import 中

详细流程说明

ConfigurationClassPostProcessor#processConfigBeanDefinitions

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	
	// ...

	// 开始解析配置类
	// 逐个解析配置类的 @PropertySource, @ComponentScan, @Import, @ImportResource, @Bean 方法
	// @ComponentScan 扫描 basePackages, 扫描到的组件会注册成为 BeanDefinition, 如果是配置类则递归解析
	// @Import 导入的类会分成3种,
	// 		ImportSelector 中延迟的被暂存起来, 非延迟的被递归解析,
	// 		ImportBeanDefinitionRegistrar 的被暂存起来,
	// 		都不是的会被当做配置类走配置类解析流程
	// @ImportResource 导入的 xml 也被暂存起来
	// @Bean 注解的方法也被暂存起来
	parser.parse(candidates);

	// 将上述暂存起来的都统一处理
	// 取到 parser 里面解析过的配置类
	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());
	}

	// 用一个 reader 从被解析过的配置类中读取并注册 BeanDefinition
	// ImportBeanDefinitionRegistrar 的后续注册 BeanDefinition 就在这里
	// DeferredImportSelector 的后续处理 还没有找到
	this.reader.loadBeanDefinitions(configClasses);
	
	// ...

}

ConfigurationClassParser#doProcessConfigurationClass 解析 @Import 部分

// Process any @Import annotations
// 处理 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

ConfigurationClassParser#getImports

从当前配置类起, 递归查找类上的 @Import 注解, 以及类上非 @Import 注解(注解类名以java开头的过滤掉)里面的 @Import 注解. 拿到所有 @Import 注解导入的类. 比如当前解析的是配置类, 配置类 上 @Import(A.class), A 上 @Import(B.class), 配置类 上还有一个 @Xxxxx 注解, 这个注解里 @Import(C.class), 最终拿到的是 A.class, C.class

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
	Set<SourceClass> imports = new LinkedHashSet<>();
	Set<SourceClass> visited = new LinkedHashSet<>();
	collectImports(sourceClass, imports, visited);
	return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
		throws IOException {

	// 拿到 sourceClass 上的 @Import 注解的 导入类,
	// 以及 sourceClass 上的其他注解内引入的 @Import 注解的 导入类,
	// 以及 sourceClass 上的其他注解内的其他注解内引入的 @Import 注解的 导入类
	// 以及 sourceClass 上的其他注解内的其他注解内的其他注解内引入的 @Import 注解的 导入类
	// ......
	// 递归层层拿
	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"));
	}
}

ConfigurationClassParser#processImports

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			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) {

				// 被导入的 是 ImportSelector (导入型选择器: 用于选择需要导入的类)
				// 判断的其实是 候选组件类 是否是 ImportSelector 或 候选组件类 是否是 ImportSelector 的父类
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					
					// 将这个 ImportSelector 实例化, 如有必要则注入4个 Aware
					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);
					}

					// 如果是延迟导入 DeferredImportSelector (延迟导入型选择器: 用于选择需要导入的类, 被导入的类的解析会延迟)
					// 会把该导入型选择器存在 ConfigurationClassParser 的 deferredImportSelectors 中
					if (selector instanceof DeferredImportSelector) {
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}

					// 不是延迟导入
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);

						// 递归导入该类导入的类再导入的类
						// 递归解析的最后一定是 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 的类
						processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
					}
				}

				// 被导入的 是 ImportBeanDefinitionRegistrar (导入型 BeanDefinition 注册器: 用于注册需要导入的 BeanDefinition)
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();

					// 将这个 ImportBeanDefinitionRegistrar 实例化, 如有必要则注入4个 Aware
					ImportBeanDefinitionRegistrar registrar =
							ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
									this.environment, this.resourceLoader, this.registry);

					// 将这个 ImportBeanDefinitionRegistrar 添加到 configClass 的 importBeanDefinitionRegistrars 中
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}

				// 被导入的 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar
				// 将其作为一个配置类来解析, 走解析 @Configuration 相同的流程
				// 并没有直接生成 BeanDefinition 并注册, 而是先保存在 ConfigurationClassParser 的 importStack 的 import 中
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					System.out.println(this.importStack.imports);

					// 将被导入的类的信息添加到 ConfigurationClassParser 的 importStack 的 import 中
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					System.out.println(this.importStack.imports);

					// 将被导入的类当成一个配置类来解析, 走解析 @Configuration 相同的流程
					processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
				}
			}
		}
		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();
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值