当前位置: 首页>后端>正文

Import注解

@Import注解
@Import是Spring基于 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@ImportConfigB:

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的优先于本身的的类定义加载。

  1. 直接初始化其他类的Bean
    在Spring 4.2之后,@Import可以直接指定实体类,加载这个类定义到context中。 例如把上面代码中的ConfigA的@Import修改为@Import(ServiceB.class),就会生成ServiceB的Bean到容器上下文中,之后运行main方法,输出为:ServiceB.证明@Import的优先于本身的的类定义加载.

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

需要实现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的非注解类
//因为AnnotationMetadataImport注解所在的类属性,如果所在类是注解类,则延伸至应用这个注解类的非注解类为止
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

  1. 指定实现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,否则一定会生成ConfigA的ServiceInterface

package com.test;
@EnableService(name = "TestServiceC")
@Configuration
class ConfigA {
// @Bean
// @ConditionalOnMissingBean
// public ServiceInterface getServiceA() {
// return new ServiceA();
// }
}
之后运行main,输出:TestServiceC

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

AbstractApplicationContext的refresh方法

-> 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.ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法实现,功能是将上面解析的配置转换为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"));
}
}
采集好之后,就可以解析了。

  1. 解析@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注解就加载解析完成了,这是一个大的递归过程。

  1. 转换为BeanDefinition注册到容器
    org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法:

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加载。


https://www.xamrdz.com/backend/3d81944798.html

相关文章: