@Import注解 -【Spring底层原理】

blog50

1、注解用法

@Import注解也是用来给容器注册组件的,使用@Import注解快速给容器中导入一个组件有三种方法java

  1. 导入@Configuration注解的配置类使用@Import(要导入到容器中的组件):容器中就会自动注册这个组件,ID默认为全类名
  2. 导入ImportSelector的实现类:经过实现ImportSelector类,实现selectImports方法,返回须要导入组件的全类名数组
  3. 导入ImportBeanDefinitionRegistrar的实现类:经过实现ImportBeanDefinitionRegistrar类,实现registerBeanDefinitions方法手动注册Bean到容器中
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}
复制代码

经过注解源码能够看到,@Import注解做用在类上,而且参数能够是class类型的数组,从这里能够看出可使用@Import注解一次导入多个组件到容器中数组

2、实例分析

从上面的注解用法来看,使用@Import注解给容器导入组件有三种方法,而且该注解做用在方法上,一次能够导入多个组件,所以,这里咱们直接将三种方法都放在一个@Import注解来进行导入。以下案例需求:使用方法一注入User类、使用方法二注入Person类、使用方法三注入Animal类。markdown

【1】导入@Configuration注解的配置类使用@Importapp

// 启动类,经过打印容器中的Bean来判断是否注入
@Test
public void TestMain(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    String[] beanNames = applicationContext.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        System.out.println(beanName);
    }
}

// 待注入的User
public class User {
}

// 配置类
@Configuration
@Import(User.class)     //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}
复制代码

经过在配置类上使用@Import注解,将User给注入进容器中,运行启动类,能够看到容器中有User对象:oop

image-20210226164625069

【2】导入ImportSelector的实现类this

导入ImportSelector的实现类须要实现ImportSelector类,自定义逻辑返回须要导入的组件,返回的字符串数组便是要注入的组件,添加修改以下代码:spa

// ImportSelector实现类
public class MyImportSelector implements ImportSelector {
    /** * @description 获取要导入到容器的组件全类名 * @author ONESTAR * @date 2021/2/25 15:49 * @param annotationMetadata:当前标注@Import注解类的全部注解信息 * @throws * @return java.lang.String[] */
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"bean.Person"};
    }
}

// 待注入的Person
public class Person {
}

// 配置类
@Configuration
@Import({User.class, MyImportSelector.class})     //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}
复制代码

ImportSelector实现类中获取要导入到容器的组件全类名,这里将ImportSelector实现类在配置类中使用@Import注解进行配置,运行启动类,能够看到容器中有Person对象:.net

image-20210227151242060

【3】导入ImportBeanDefinitionRegistrar的实现类code

导入ImportBeanDefinitionRegistrar的实现类须要实现ImportBeanDefinitionRegistrar类,经过实现registerBeanDefinitions方法手动注册Bean到容器中,添加修改以下代码:orm

// ImportBeanDefinitionRegistrar实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        // 指定Bean的名称
        RootBeanDefinition beanDefinition = new RootBeanDefinition(Animal.class);
        beanDefinitionRegistry.registerBeanDefinition("Animal", beanDefinition);
    }
}

// 待注入的Animal
public class Animal {
}

// 配置类
@Configuration
@Import({User.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})     //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}
复制代码

经过ImportBeanDefinitionRegistrar的实现类进行手动注册添加Bean,并在配置类中使用@Import注解进行配置,运行启动类,能够看到容器中有Animal对象:

image-20210227153057676

3、源码追踪

参考:blog.csdn.net/mamamalulul…

经过@Configuration注解,会进入到doProcessConfigurationClass方法,此时解析的是appConfigure,在doProcessConfigurationClass方法里面,有个执行@Import注解的方法,即processImports

this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
复制代码

@Import注解执行的时机,解析配置类的时候,由ConfigurationClassParser当中的processImports来处理,在分析processImports方法以前,我们先来看看参数getImports方法:

【1】getImports方法

进入源码查看方法,这个方法就是获取全部的@import 里面的类,流程以下:

  1. 定义一个 visited 的集合,用做 是否已经 判断过的标志
  2. 这里就是获取sourceClass 上面的 全部的 annotation,并挨个判断, 若是不是 @import ,那就 进一步递归 调用 对应的 annotation,直到所有结束
  3. 加载sourceClass 里面 的@Import annotation 里面对应的类名 ,最后返回
// 获取全部的`@import` 里面的类
private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
    Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
    Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
    this.collectImports(sourceClass, imports, visited);
    return imports;
}

// 这里就是获取sourceClass 上面的 全部的 annotation, 若是不是 @import ,那就 进一步递归 调用 对应的 annotation,直到所有结束
private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException {
    if (visited.add(sourceClass)) {
        Iterator var4 = sourceClass.getAnnotations().iterator();

        while(var4.hasNext()) {
            ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next();
            String annName = annotation.getMetadata().getClassName();
            if (!annName.equals(Import.class.getName())) {
                this.collectImports(annotation, imports, visited);
            }
        }

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

}
复制代码

【2】processImports 方法

获取到@Import里面的类后,再来执行processImports方法,进入源码进行查看:

private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
    // 准备注入的候选类集合为空 直接返回
    if (!importCandidates.isEmpty()) {
        // 循环注入的检查
        if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
        } else {
            // 当前configClass加入到ImportStack里面
            this.importStack.push(configClass);

            try {
                Iterator var6 = importCandidates.iterator();

                // 遍历注入的候选集合
                while(var6.hasNext()) {
                    ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
                    Class candidateClass;
                    // 若是是实现了ImportSelector接口的类
                    if (candidate.isAssignable(ImportSelector.class)) {
                        candidateClass = candidate.loadClass();
                        ImportSelector selector = (ImportSelector)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 {
                            // 调用selector当中的selectImports方法,获得要注入的类的全限定名
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
                            this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                        }
                        // 若是是ImportBeanDefinitionRegistrar 则configClass.addImportBeanDefinitionRegistrar 提早放到一个map当中
                    } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    } else {
                        // 若是是普通类
                        this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                        this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                    }
                }
            } catch (BeanDefinitionStoreException var17) {
                throw var17;
            } catch (Throwable var18) {
                throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);
            } finally {
                this.importStack.pop();
            }
        }

    }
}
复制代码

以上方法即是@Import注解的核心做用,能够总结为如下几点:

  1. 判断注入的候选集合 importCandidates 是否为空,为空则直接退出
  2. 循环注入检查:判断isChainedImportOnStack ,若是为true ,加入 problemReporter 里面的error ,并退出
  3. 把当前的 configClass 加入到 ImportStack里面,ImportStack 是继承了 ArrayDeque 和实现了 ImportRegistry
  4. 遍历注入的候选集合:对 getImports 里面获取到的 须要import 的类 进行遍历 处理(三种类型的类执行不一样的逻辑)
    1. 实现了ImportSelector接口的类,调用getExclusionFilter()方法,若是不为空,那么就进行过滤,过滤后调用selectImports方法,获得要注入的类的全限定名。根据类全限定名,获得类元信息。而后递归的调用processImports方法
    2. 实现了ImportBeanDefinitionRegistrar接口的类,会实例化这个类,放入集合importBeanDefinitionRegistrars当中
    3. 普通类型的类(上面两个都不知足),那么就把它看成是配置类来处理,调用processConfigurationClass方法,最终会放入到configurationClasses这个集合当中。
  5. 若是是 ImportBeanDefinitionRegistrar 类型,这里也是 先实例一个对象,而后加入到 importBeanDefinitionRegistrars 里面,后续 会在 ConfigurationClassBeanDefinitionReader 这个类里面 的 loadBeanDefinitionsFromRegistrars 方法处理的
  6. 若是上面两种类型都不是,那就是当初普通的 带有@Configuration 的类进行处理了
相关文章
相关标签/搜索