美文网首页
spring源码解析------@Import注解解析与Impo

spring源码解析------@Import注解解析与Impo

作者: szhlcy | 来源:发表于2019-10-27 16:06 被阅读0次

    1.@Import注解在springBoot中间接的广泛应用

     在springboot中并没有直接显式的使用@Import标签,而是通过@Import标签来间接的提供了很多自动配置的注解。比如@EnableAutoConfiguration@EnableConfigurationProperties等。这些标签的实现都是通过使用@Import标签来完成的。

    ......
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
        ......
    }
    
    ......
    @Import(EnableConfigurationPropertiesRegistrar.class)
    public @interface EnableConfigurationProperties {
        ......
    }
    

     可以发现都是通过@Import来完成的。

    2.spring中的@Import注解

    2.1@Import注解的作用

    @Import标签可以导入一个或者多个组件类,通常是@Configuration 注入的bean。提供了与xml中<import/>标签类型的功能,能够导入@Configuration类。

    public @interface Import {
    
        /**
         * {@link Configuration @Configuration}, {@link ImportSelector},
         * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
         */
        Class<?>[] value();
    
    }
    

     导入的类可以是@Configuration类型的配置类,实现了ImportSelector接口的类,实现了ImportBeanDefinitionRegistrar的类或者常规的组件类。

    2.2@Import的解析前的处理

    @Import处理的位置其实跟@Conditional注解都在同一个类中,处理的时机也是一样的。这里可以去看看@Conditional注解解析的逻辑

    2.2.1 容器的刷新时候的准备

     在容器刷新方法就定义在AbstractApplicationContext类的refresh方法中。在这个里面会有解析注册以及实例话bean和其他的步骤,我们要看的就是解析跟注册步骤。

        public void refresh() throws BeansException, IllegalStateException {
        ......
        invokeBeanFactoryPostProcessors(beanFactory);
        }
    

     在invokeBeanFactoryPostProcessors方法中会实例化所有的BeanFactoryPostProcessor类型的类并调用实现的postProcessBeanFactory方法。

    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
        ......
    }
    
    2.2.2 PostProcessorRegistrationDelegate处理BeanDefinitionRegistry以及BeanFactoryPostProcessor

     这里直接进入到invokeBeanFactoryPostProcessors方法。会有两种处理方式:

    1. 当前的beanFactoryBeanDefinitionRegistry类型的
      (1) 先处理beanFactory,然后按照是否实现了PriorityOrdered,Ordered以及两个都没有实现的顺序来处理BeanDefinitionRegistryPostProcessor接口的实现类。
      (2)处理实现了BeanFactoryPostProcessor接口的子类依次调用实现的postProcessBeanFactory方法
    2. 当前的beanFactory不是BeanDefinitionRegistry类型的,则直接处理实现了BeanFactoryPostProcessor接口的子类依次调用实现的postProcessBeanFactory方法

     其中会处理@Import标签的ConfigurationClassPostProcessor实现类BeanDefinitionRegistryPostProcessor接口跟PriorityOrdered接口间接实现了BeanFactoryPostProcessor接口因此无论怎么样都会被调用的。

        public static void invokeBeanFactoryPostProcessors(
                ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
            //如果当前的beanFactory是BeanDefinitionRegistry的,则需要将已经存在的beanDefinition进行注册
            if (beanFactory instanceof BeanDefinitionRegistry) {
                BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
                List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
                //保存注册bean的BeanDefinitionRegistryPostProcessor
                List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
                //迭代beanFactoryPostProcessors如果是BeanDefinitionRegistryPostProcessor子类则加入到registryProcessors集合中
                for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                        BeanDefinitionRegistryPostProcessor registryProcessor =
                                (BeanDefinitionRegistryPostProcessor) postProcessor;
                        //处理beanFactory中的beanDefinitionNames
                        registryProcessor.postProcessBeanDefinitionRegistry(registry);
                        registryProcessors.add(registryProcessor);
                    }
                    else {
                        regularPostProcessors.add(postProcessor);
                    }
                }
                List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
                //先处理同时实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的实现类
                String[] postProcessorNames =
                        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    //如果当前的BeanDefinitionRegistryPostProcessor类的实现类也实现了PriorityOrdered类,则加入当当前需要注册的BeanDefinitionRegistryPostProcessor集合
                    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                    }
                }
                //进行排序
                sortPostProcessors(currentRegistryProcessors, beanFactory);
                //加入到registryProcessors集合
                registryProcessors.addAll(currentRegistryProcessors);
                //调用实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
                currentRegistryProcessors.clear();
                ......
                invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
                invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }else {
                // Invoke factory processors registered with the context instance.
                invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }
    }
    
    2.2.3 ConfigurationClassPostProcessorprocessConfigBeanDefinitionsprocessConfigBeanDefinitions解析bean

     上面提到了ConfigurationClassPostProcessor无论怎么样都会被调用。这里先看看实现的postProcessBeanDefinitionRegistry方法跟postProcessBeanFactory的共同点。

        //在refresh方法中最先被调用
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            //给当前的BeanDefinitionRegistry类型的beanFactory设置一个全局hash值,避免重复处理
            int registryId = System.identityHashCode(registry);
            if (this.registriesPostProcessed.contains(registryId)) {
                throw new IllegalStateException(
                        "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
            }
            if (this.factoriesPostProcessed.contains(registryId)) {
                throw new IllegalStateException(
                        "postProcessBeanFactory already called on this post-processor against " + registry);
            }
            //加入到已经处理的PostProcessed集合中
            this.registriesPostProcessed.add(registryId);
            //进行处理
            processConfigBeanDefinitions(registry);
        }
    
        /**
         * Prepare the Configuration classes for servicing bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
         */
        //在refresh方法中第二被调用
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            //给当前的ConfigurableListableBeanFactory类型的beanFactory设置一个全局hash值,避免重复处理
            int factoryId = System.identityHashCode(beanFactory);
            if (this.factoriesPostProcessed.contains(factoryId)) {
                throw new IllegalStateException(
                        "postProcessBeanFactory already called on this post-processor against " + beanFactory);
            }
            //加入到已经处理的集合中
            this.factoriesPostProcessed.add(factoryId);
            //如果当前的beanFactory不在registriesPostProcessed中则进行处理
            if (!this.registriesPostProcessed.contains(factoryId)) {
                // BeanDefinitionRegistryPostProcessor hook apparently not supported...
                // Simply call processConfigurationClasses lazily at this point then.
                processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
            }
            //将beanFactory中用Configuration注解配置的bean进行动态加强
            enhanceConfigurationClasses(beanFactory);
            //增加一个ImportAwareBeanPostProcessor
            beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
        }
    

     发现一个共同点就是,如果没有解析过的beanFactory进来都会调用processConfigBeanDefinitions方法来处理Configuration类。在这个方法中有两个解析的位置。

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    //循环处理知道所有的bean都处理完,包含bean内部定义的bean
            do {
                //第一个解析的位置,这里是解析能够直接获取的候选配置bean。可能是Component,ComponentScan,Import,ImportResource或者有Bean注解的bean
                parser.parse(candidates);
                parser.validate();
                //获取上面封装已经解析过的配置bean的ConfigurationClass集合
                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());
                }
                //第二个解析的位置,这里是加载configurationClasse中内部可能存在配置bean,比如方法上加了@Bean或者@Configuration标签的bean
                this.reader.loadBeanDefinitions(configClasses);
                ......
                }
                ......
    }
    

     两个解析的位置分别是:

    1. 第一个位置是ConfigurationClassParser类的parse方法,解析能够直接从beanFactory中获取的候选bean。
    2. 第二个是ConfigurationClassBeanDefinitionReaderloadBeanDefinitions方法,解析从直接获取的候选bean中内部定义的bean。
    2.2.4 ConfigurationClassParser的解析过程

    ConfigurationClassParserparse有很多重载的方法,但是内部逻辑都是调用的processConfigurationClass方法。

        public void parse(Set<BeanDefinitionHolder> configCandidates) {
            for (BeanDefinitionHolder holder : configCandidates) {
                BeanDefinition bd = holder.getBeanDefinition();
                try {
                //解析bean
                    if (bd instanceof AnnotatedBeanDefinition) {
                        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类型的实现类
            this.deferredImportSelectorHandler.process();
        }
    
        protected final void parse(@Nullable String className, String beanName) throws IOException {
            ......
            processConfigurationClass(new ConfigurationClass(reader, beanName));
        }
    
        protected final void parse(Class<?> clazz, String beanName) throws IOException {
            processConfigurationClass(new ConfigurationClass(clazz, beanName));
        }
    
        protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
            processConfigurationClass(new ConfigurationClass(metadata, beanName));
        }
    

     从上面可以看出来,主要要看的逻辑还是在processConfigurationClass方法中。这里还需要注意一点就是在最上面的parse方法最后有一个逻辑this.deferredImportSelectorHandler.process()这个逻辑是处理@Import注解中指定的引入类是DeferredImportSelectorHandler子类的情况的。
     进入到processConfigurationClass方法,关于这个方法在@Conditional注解解析的逻辑这个里面说到过。这里只需要关注内部的doProcessConfigurationClass方法的逻辑。

        protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
            //检查当前解析的配置bean是否包含Conditional注解,如果不包含则不需要跳过
            // 如果包含了则进行match方法得到匹配结果,如果是符合的并且设置的配置解析策略是解析阶段不需要调过
            if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
                return;
            }
            //从缓存中尝试获取当前配置bean解析之后的ConfigurationClass对象
            ConfigurationClass existingClass = this.configurationClasses.get(configClass);
            if (existingClass != null) {
                //检查当前这个配置bean是通过@Import标签引入的还是自动注入到另外一个配置类bean里面的
                if (configClass.isImported()) {
                    //如果是通过@Import标签引入则将当前解析的配置bean加入到已经存在的解析过的bean的用来保存通过@Import标签引入的bean的集合中
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }
                    // Otherwise ignore new imported config class; existing non-imported class overrides it.
                    return;
                }
                else {
                    // Explicit bean definition found, probably replacing an import.
                    // Let's remove the old one and go with the new one.
                    //将当前解析的配置bean代替之前的
                    this.configurationClasses.remove(configClass);
                    this.knownSuperclasses.values().removeIf(configClass::equals);
                }
            }
    
            // Recursively process the configuration class and its superclass hierarchy.
            //递归获取原始的配置类信息然后封装为SourceClass
            SourceClass sourceClass = asSourceClass(configClass);
            do {
                //处理configClass
                sourceClass = doProcessConfigurationClass(configClass, sourceClass);
            }
            while (sourceClass != null);
            //保存到configurationClasses中
            this.configurationClasses.put(configClass, configClass);
        }
    

     在doProcessConfigurationClass方法中有很多标签的处理逻辑,比如@Component@PropertySources@ComponentScans等。对于@Import注解的处理方式,封装在了另外的一个方法processImports里面。

        protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
                throws IOException {
                    .......
                            processImports(configClass, sourceClass, getImports(sourceClass), true);
                    ......
                }
    
    2.3 @Import注解的解析
    2.3.1 processImports方法

     已经知道@Import注解在那个方法里面进行解析了。先看看对应的方法参数的含义。

    参数 含义
    ConfigurationClass configClass 当前需要解析的Configuration类
    SourceClass currentSourceClass 类的源数据封装对象
    Collection<SourceClass> importCandidates Configuration类引入的其他类,以及其他类中引入的别的类,比如A引入B,B引入C,C引入D。那么这个就包含了B,C,D
    boolean checkForCircularImports 是否检查循环引入

     现在对方法内部进行分析

        private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
            //检查包含有Import注解的集合是不是空的,空的则表示没有
            if (importCandidates.isEmpty()) {
                return;
            }
            //检查是否存在循环引入,通过deque的方式来检查
            if (checkForCircularImports && isChainedImportOnStack(configClass)) {
                this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
            }
            else {
                //将当前bean放到importStack中,用于检查循环引入
                this.importStack.push(configClass);
                try {
                    for (SourceClass candidate : importCandidates) {
                        //import指定的Bean是ImportSelector类型
                        if (candidate.isAssignable(ImportSelector.class)) {
                            //实例化指定的ImportSelector子类
                            Class<?> candidateClass = candidate.loadClass();
                            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                    this.environment, this.resourceLoader, this.registry);
                            //如果是ImportSelector的子类DeferredImportSelector的子类则按照DeferredImportSelectorHandler逻辑进行处理
                            if (selector instanceof DeferredImportSelector) {
                                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                            }
                            else {//如果不是则获取指定的需要引入的class的ClassNames
                                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                //根据ClassNames获取并封装成一个SourceClass
                                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                                //继续调用processImports处理
                                processImports(configClass, currentSourceClass, importSourceClasses, false);
                            }
                        }//如果是ImportBeanDefinitionRegistrar的子类
                        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                            // Candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
                            //如果对应的ImportBeanDefinitionRegistrar子类对象,并放到configClass,这个是用来注册额外的bean的
                            Class<?> candidateClass = candidate.loadClass();
                            ImportBeanDefinitionRegistrar registrar =
                                    ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                            this.environment, this.resourceLoader, this.registry);
                            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                        }
                        else {//不是上面任何类的子类就可以进行处理了,将指定的需要引入的bean转化为ConfigurationClass,然后到processConfigurationClass方法中处理
                            this.importStack.registerImport(
                                    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                            processConfigurationClass(candidate.asConfigClass(configClass));
                        }
                    }
                }
                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();
                }
            }
        }
    

     主要的步骤如下:

    1. 检查解析出来的Import注解集合是不是空的,空的则不处理,直接返回
    2. 如果设置了需要检查循环依赖,则进行循环依赖的检查,不需要则跳过,进入3步骤
    3. 进行处理
      (1) 将当前bean放到importStack中,用于检查循环引入
      (2)循环处理以下情况
        1)如果是ImportSelector类型的子类。先实例化这个类,然后检查这个类是不是DeferredImportSelector子类,是的则调用ConfigurationClassParser的内部类DeferredImportSelectorHandlerhandle方法处理。如果不是DeferredImportSelector子类则继续调用processImports方法处理。按照步骤1从头开始。
        2)如果是ImportBeanDefinitionRegistrar类型的子类,则实例化这个类,然后放到当前解析的bean的importBeanDefinitionRegistrars属性中,后面注册bean时候调用。
        3)没有实现以上任何接口,则将当前的被引入的bean加入到这个bean的importedBy属性中,然后调用processConfigurationClass方法。

     对上面的信息进行总结。这个方法作用就是解析时候将被用@Import注解引入的bean加入到使用@Import注解标签的bean的importedBy中后面进行解析时候用,还有就是后面注册bean的时候可能也会调用实现了ImportBeanDefinitionRegistrar类型的子类。

    2.3.2 processConfigurationClass方法处理configClass

     上面processImports方法调用之后最后都会返回到processConfigurationClass。这里介绍以下这个方法的作用。这个方法会对ConfigurationClass对象(这个对象封装了贴了@Configuration或者@Bean注解的对象的信息)进行解析,解析上面的注解。就我们上面提到的@Component@PropertySources@ComponentScans等。最后保存起来,后面实例化的时候会用到(后面实例化的时候使用的是CGLIB的方式实例化的),跟其他的bean不一样。

        protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
            ......
            do {
                //处理configClass
                sourceClass = doProcessConfigurationClass(configClass, sourceClass);
            }
            while (sourceClass != null);
            //保存到configurationClasses中
            this.configurationClasses.put(configClass, configClass);
        }
    

     到这里@Import注解的解析基本就完了。剩下的就是介绍ImportSelectorImportBeanDefinitionRegistrar以及DeferredImportSelector之间的区别了。下篇文章解析。

    3.ImportSelectorImportBeanDefinitionRegistrar以及DeferredImportSelector

     在上面的processImports方法中已经讲解了对所有的@Import注解中value值为不同指的情况进行解析。有以下的情况:

    1. ImportSelector接口的实现类
    2. DeferredImportSelector接口的实现类
    3. ImportBeanDefinitionRegistrar接口的实现类
    4. 非以上3中接口的实现类,也就是普通的类

     这里主要讲解1到3这三个接口类的区别。

    3.1 ImportSelector
    public interface ImportSelector {
    
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    
    }
    

    ImportSelector接口作用将方法返回的字符串数组作为bean注入到容器中,注意这里的字符串需要是对象的全路径名称比如A.class.getName()这种。后面会讲spring的扩展的时候会使用的。

    3.2 ImportBeanDefinitionRegistrar

    ImportBeanDefinitionRegistrar这个接口作用是,用户可以实现了之后来自定义来注册需要注册的bean。可以设置自定义的BeanNameGeneratorbean名称生成规则。

    public interface ImportBeanDefinitionRegistrar {
    
        default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
                BeanNameGenerator importBeanNameGenerator) {
    
            registerBeanDefinitions(importingClassMetadata, registry);
        }
    
        default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        }
    
    }
    
    3.3 DeferredImportSelector

    ImportBeanDefinitionRegistrarImportSelector的子接口,在4.0版本加入的。在这个类里面添加分组的功能,能够将多个DeferredImportSelector进行分组。同一个组内的ImportBeanDefinitionRegistrar能够通过实现@Order注解去实现排序。在调用的时候处理的时候会先按照序号进行排序,然后依次调用对应实现的 ImportSelector接口的selectImports方法。
     还有一点就是DeferredImportSelector的调用逻辑在,所有的@Configuration已经解析了之后在调用的。这点可以在2.2.4 ConfigurationClassParser的解析过程代码中看出来。

    public interface DeferredImportSelector extends ImportSelector {
    
        default Class<? extends Group> getImportGroup() {
            return null;
        }
    
        interface Group {
    
        ......
    
        }
    

     这里将区别列举出来

    作用
    ImportSelector 将方法返回的字符串数组作为bean注入到容器中
    ImportBeanDefinitionRegistrar 自定义来注册bean
    DeferredImportSelector ImportSelector差不多只不过多了分组的功能,处理在@Configuration类型bean解析之后

    相关文章

      网友评论

          本文标题:spring源码解析------@Import注解解析与Impo

          本文链接:https://www.haomeiwen.com/subject/epxmvctx.html