Spring全解系列@Import注解

编程

本文基于 Spring 5.2.x

@Import注解

@ImportSpring基于 Java 注解配置的主要组成部分。@Import注解提供了@Bean注解的功能,同时还有原来Spring基于 xml 配置文件里的<import>标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration的类。

下面将分别说明@Import注解的功能。

1. 引入其他的@Configuration

假设有如下接口和两个实现类:

package com.test

interface ServiceInterface {

void test();

}

class ServiceA implements ServiceInterface {

@Override

public void test() {

System.out.println("ServiceA");

}

}

class ServiceB implements ServiceInterface {

@Override

public void test() {

System.out.println("ServiceB");

}

}

两个@Configuration,其中ConfigA``@Import``ConfigB:

package com.test

@Import(ConfigB.class)

@Configuration

class ConfigA {

@Bean

@ConditionalOnMissingBean

public ServiceInterface getServiceA() {

return new ServiceA();

}

}

@Configuration

class ConfigB {

@Bean

@ConditionalOnMissingBean

public ServiceInterface getServiceB() {

return new ServiceB();

}

}

通过ConfigA创建AnnotationConfigApplicationContext,获取ServiceInterface,看是哪种实现:

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);

ServiceInterface bean = ctx.getBean(ServiceInterface.class);

bean.test();

}

输出为:ServiceB.证明@Import的优先于本身的的类定义加载。

2. 直接初始化其他类的Bean

在Spring 4.2之后,@Import可以直接指定实体类,加载这个类定义到context中。

例如把上面代码中的ConfigA@Import修改为@Import(ServiceB.class),就会生成ServiceBBean到容器上下文中,之后运行main方法,输出为:ServiceB.证明@Import的优先于本身的的类定义加载.

3. 指定实现ImportSelector(以及DefferredServiceImportSelector)的类,用于个性化加载

指定实现ImportSelector的类,通过AnnotationMetadata里面的属性,动态加载类。AnnotationMetadataImport注解所在的类属性(如果所在类是注解类,则延伸至应用这个注解类的非注解类为止)。

需要实现selectImports方法,返回要加载的@Configuation或者具体Bean类的全限定名的String数组。

package com.test;

class ServiceImportSelector implements ImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

//可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称

return new String[]{"com.test.ConfigB"};

}

}

@Import(ServiceImportSelector.class)

@Configuration

class ConfigA {

@Bean

@ConditionalOnMissingBean

public ServiceInterface getServiceA() {

return new ServiceA();

}

}

再次运行main方法,输出:ServiceB.证明@Import的优先于本身的的类定义加载。

一般的,框架中如果基于AnnotationMetadata的参数实现动态加载类,一般会写一个额外的Enable注解,配合使用。例如:

package com.test;

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Target(ElementType.TYPE)

@Import(ServiceImportSelector.class)

@interface EnableService {

String name();

}

class ServiceImportSelector implements ImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

//这里的importingClassMetadata针对的是使用@EnableService的非注解类

//因为`AnnotationMetadata`是`Import`注解所在的类属性,如果所在类是注解类,则延伸至应用这个注解类的非注解类为止

Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);

String name = (String) map.get("name");

if (Objects.equals(name, "B")) {

return new String[]{"com.test.ConfigB"};

}

return new String[0];

}

}

之后,在ConfigA中增加注解@EnableService(name = "B")

package com.test;

@EnableService(name = "B")

@Configuration

class ConfigA {

@Bean

@ConditionalOnMissingBean

public ServiceInterface getServiceA() {

return new ServiceA();

}

}

再次运行main方法,输出:ServiceB.

还可以实现DeferredImportSelector接口,这样selectImports返回的类就都是最后加载的,而不是像@Import注解那样,先加载。

例如:

package com.test;

class DefferredServiceImportSelector implements DeferredImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);

String name = (String) map.get("name");

if (Objects.equals(name, "B")) {

return new String[]{"com.test.ConfigB"};

}

return new String[0];

}

}

修改EnableService注解:

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Target(ElementType.TYPE)

@Import(DefferredServiceImportSelector.class)

@interface EnableService {

String name();

}

这样ConfigA就优先于DefferredServiceImportSelector返回的ConfigB加载,执行main方法,输出:ServiceA

4. 指定实现ImportBeanDefinitionRegistrar的类,用于个性化加载

ImportSelector用法与用途类似,但是如果我们想重定义Bean,例如动态注入属性,改变Bean的类型和Scope等等,就需要通过指定实现ImportBeanDefinitionRegistrar的类实现。例如:

定义ServiceC

package com.test;

class ServiceC implements ServiceInterface {

private final String name;

ServiceC(String name) {

this.name = name;

}

@Override

public void test() {

System.out.println(name);

}

}

定义ServiceImportBeanDefinitionRegistrar动态注册ServiceC,修改EnableService

package com.test;

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Target(ElementType.TYPE)

@Import(ServiceImportBeanDefinitionRegistrar.class)

@interface EnableService {

String name();

}

class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

@Override

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);

String name = (String) map.get("name");

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)

//增加构造参数

.addConstructorArgValue(name);

//注册Bean

registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());

}

}

并且根据后面的源代码解析可以知道,ImportBeanDefinitionRegistrar@Bean 注解之后加载,所以要修改ConfigA去掉其中被@ConditionalOnMissingBean注解的Bean,否则一定会生成ConfigAServiceInterface

package com.test;

@EnableService(name = "TestServiceC")

@Configuration

class ConfigA {

// @Bean

// @ConditionalOnMissingBean

// public ServiceInterface getServiceA() {

// return new ServiceA();

// }

}

之后运行main,输出:TestServiceC

@Import相关源码解析

加载解析@Import注解位于BeanFactoryPostProcessor处理的时候:

AbstractApplicationContextrefresh方法

-> invokeBeanFactoryPostProcessors(beanFactory);

-> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

-> registryProcessor.postProcessBeanDefinitionRegistry(registry);

这里的registryProcessor,我们指ConfigurationClassPostProcessor

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(registry)

-> processConfigBeanDefinitions(registry):

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

//省略一些配置检查与设置的逻辑

//根据@Order注解,排序所有的@Configuration类

configCandidates.sort((bd1, bd2) -> {

int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());

int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());

return Integer.compare(i1, i2);

});

// 创建ConfigurationClassParser解析@Configuration类

ConfigurationClassParser parser = new ConfigurationClassParser(

this.metadataReaderFactory, this.problemReporter, this.environment,

this.resourceLoader, this.componentScanBeanNameGenerator, registry);

//剩余没有解析的@Configuration类

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);

//已经解析的@Configuration类

Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

do {

//解析

parser.parse(candidates);

parser.validate();

Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());

configClasses.removeAll(alreadyParsed);

// 生成类定义读取器读取类定义

if (this.reader == null) {

this.reader = new ConfigurationClassBeanDefinitionReader(

registry, this.sourceExtractor, this.resourceLoader, this.environment,

this.importBeanNameGenerator, parser.getImportRegistry());

}

this.reader.loadBeanDefinitions(configClasses);

alreadyParsed.addAll(configClasses);

candidates.clear();

if (registry.getBeanDefinitionCount() > candidateNames.length) {

//省略检查是否有其他需要加载的配置的逻辑

}

}

while (!candidates.isEmpty());

//省略后续清理逻辑

}

其中parser.parse(candidates)的逻辑主要由org.springframework.context.annotation.ConfigurationClassParser实现,功能是加载@Import注解还有即系@Import注解。reader.loadBeanDefinitions(configClasses);的逻辑主要由org.springframework.context.annotation.ConfigurationClassBeanDefinitionReaderloadBeanDefinitionsForConfigurationClass方法实现,功能是将上面解析的配置转换为BeanDefinition就是Bean定义。

1. 加载@Import注解

org.springframework.context.annotation.ConfigurationClassParser

首先是parse方法

public void parse(Set<BeanDefinitionHolder> configCandidates) {

for (BeanDefinitionHolder holder : configCandidates) {

BeanDefinition bd = holder.getBeanDefinition();

try {

if (bd instanceof AnnotatedBeanDefinition) {

//这里的parse实际上就是调用下面即将分析的doProcessConfigurationClass

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);

}

}

//最后处理所有的`DeferredImportSelector`,符合上面提到的`DeferredImportSelector`的功能

this.deferredImportSelectorHandler.process();

}

@Nullable

protected final SourceClass doProcessConfigurationClass(

ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)

throws IOException {

//处理`@Component`注解的MemberClass相关代码...

//处理`@PropertySource`注解相关代码...

//处理`@ComponentScan`注解相关代码...

//处理`@Import`注解:

processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

//处理`@ImportResource`注解相关代码...

//处理`@Bean`注解相关代码...

//处理接口方法相关代码...

//处理父类相关代码...

}

通过getImports方法,采集相关的@Import里面的类。

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {

Set<SourceClass> imports = new LinkedHashSet<>();

Set<SourceClass> visited = new LinkedHashSet<>();

//递归查询所有注解以及注解的注解是否包含@Import

collectImports(sourceClass, imports, visited);

return imports;

}

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();

//对于非@Import注解,递归查找其内部是否包含@Import注解

if (!annName.equals(Import.class.getName())) {

collectImports(annotation, imports, visited);

}

}

//添加@Import注解里面的所有配置类

imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));

}

}

采集好之后,就可以解析了。

2. 解析@Import注解

解析的方法是:processImports

//在解析时,入栈,解析结束后,出栈,通过检查栈中是否有当前类,判断是否有循环依赖

private final ImportStack importStack = new ImportStack();

//记录所有的ImportBeanDefinitionRegistrar

private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();

//解析也是递归方法

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,

Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,

boolean checkForCircularImports) {

if (importCandidates.isEmpty()) {

return;

}

//通过importStack检查循环依赖

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)) {

//处理ImportSelector接口的实现类

Class<?> candidateClass = candidate.loadClass();

//创建这些Selector实例

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,则用deferredImportSelectorHandler处理

if (selector instanceof DeferredImportSelector) {

this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);

}

else {

//如果不是DeferredImportSelector,调用selectImports方法获取要加载的类全限定名称,递归调用本方法继续解析

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接口的实现类

Class<?> candidateClass = candidate.loadClass();

//同样的,创建这些ImportBeanDefinitionRegistrar实例

ImportBeanDefinitionRegistrar registrar =

ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,

this.environment, this.resourceLoader, this.registry);

//放入importBeanDefinitionRegistrar,用于后面加载

configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

}

else {

//处理@Configuration注解类,或者是普通类(直接生成Bean)

//在栈加上这个类

this.importStack.registerImport(

currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());

//递归回到doProcessConfigurationClass处理@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();

}

}

}

这样,所有的@Conditional类相关的@Import注解就加载解析完成了,这是一个大的递归过程。

3. 转换为BeanDefinition注册到容器

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReaderloadBeanDefinitionsForConfigurationClass方法:

private void loadBeanDefinitionsForConfigurationClass(

ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

if (trackedConditionEvaluator.shouldSkip(configClass)) {

String beanName = configClass.getBeanName();

if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {

this.registry.removeBeanDefinition(beanName);

}

this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());

return;

}

//对Import完成的,加载其Import的BeanDefinition

if (configClass.isImported()) {

registerBeanDefinitionForImportedConfigurationClass(configClass);

}

//加载@Bean注解的方法生成的Bean的Definition

for (BeanMethod beanMethod : configClass.getBeanMethods()) {

loadBeanDefinitionsForBeanMethod(beanMethod);

}

//@ImportResource 注解加载的

loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

//加载ImportBeanDefinitionRegistrar加载的Bean的Definition

loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

}

通过这里可以看出,为啥之前说@Bean注解的Bean会优先于ImportBeanDefinitionRegistrar返回的Bean加载。

以上是 Spring全解系列@Import注解 的全部内容, 来源链接: utcz.com/z/517278.html

回到顶部