• 【Spring】——7、@Import注解给容器中快速导入一个组件


    在这里插入图片描述

    📫作者简介:zhz小白
    公众号:小白的Java进阶之路
    专业技能:
    1、Java基础,并精通多线程的开发,熟悉JVM原理
    2、熟悉Java基础,并精通多线程的开发,熟悉JVM原理,具备⼀定的线上调优经验
    3、熟悉MySQL数据库调优,索引原理等,⽇志原理等,并且有出过⼀篇专栏
    4、了解计算机⽹络,对TCP协议,滑动窗⼝原理等有⼀定了解
    5、熟悉Spring,Spring MVC,Mybatis,阅读过部分Spring源码
    6、熟悉SpringCloud Alibaba体系,阅读过Nacos,Sentinel,Seata,Dubbo,Feign,Gateway核⼼源码与设计,⼆次开发能⼒
    7、熟悉消息队列(Kafka,RocketMQ)的原理与设计
    8、熟悉分库分表ShardingSphere,具有真实⽣产的数据迁移经验
    9、熟悉分布式缓存中间件Redis,对其的核⼼数据结构,部署架构,⾼并发问题解决⽅案有⼀定的积累
    10、熟悉常⽤设计模式,并运⽤于实践⼯作中
    11、了解ElasticSearch,对其核⼼的原理有⼀定的了解
    12、了解K8s,Jekins,GitLab
    13、了解VUE,GO
    14、⽬前有正在利⽤闲暇时间做互游游戏,开发、运维、运营、推销等

    本人著作git项目:https://gitee.com/zhouzhz/star-jersey-platform,有兴趣的可以私聊博主一起编写,或者给颗star
    领域:对支付(FMS,FUND,PAY),订单(OMS),出行行业等有相关的开发领域
    🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

    1、自己写的类:

    日常工作中,我们可以把一些bean组件通过包扫描+注解(@Controller,@Service,@Respository,@Component)的形式将入注入到IOC容器中,可以定制为单例Bean或者多例Bean,当然这种方式,一般局限于我们自己写的类。

    2、三方类

    如果是三方的类,我们一般怎么注入到容器中呢,有两种方方式:@Bean,@Import可以将其注入Spring容器中。

    3、注册Bean的方式(注解方式)

    包扫描+注解(@Controller、@Servcie、@Repository、@Component)
    @Bean注解,通常用于导入第三方包中的组件
    @Import注解,快速向Spring容器中导入一个组件

    4、@Import注解概述

    • Spring3.0之前我们可以通过XML方式去指定扫描特定的包将类注入到Spring容器中,Spring3.0之后通过JavaConfig的方式将Bean的元信息以代码的方式进行描述,我们可以通过@Configuration与@Bean配合使用将原来配置在XML方文件里面的Bean通过Java代码描述。
    • @Import注解提供了@Bean注解的功能,同时还有XML配置文件里面标签组织多个分散的XML文件的功能,当然在这里是组织多个分散的@Configuration,因为一个配置类就约等于一个XML配置文件。

    接下来让我们开始走进@Import的世界,先看其源码:

    /*
     * Copyright 2002-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * Indicates one or more component classes to import — typically
     * {@link Configuration @Configuration} classes.
     *
     * 

    Provides functionality equivalent to the {@code } element in Spring XML. * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}). * *

    {@code @Bean} definitions declared in imported {@code @Configuration} classes should be * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired} * injection. Either the bean itself can be autowired, or the configuration class instance * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly * navigation between {@code @Configuration} class methods. * *

    May be declared at the class level or as a meta-annotation. * *

    If XML or other non-{@code @Configuration} bean definition resources need to be * imported, use the {@link ImportResource @ImportResource} annotation instead. * * @author Chris Beams * @author Juergen Hoeller * @since 3.0 * @see Configuration * @see ImportSelector * @see ImportBeanDefinitionRegistrar * @see ImportResource */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration @Configuration}, {@link ImportSelector}, * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import. */ Class<?>[] value(); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    我们从其中源码可以看出@Import是可以与@Configuration、ImportSelector、ImportBeanDefinitionRegistrar配合使用,并且可以看出其只能放到类上,不能放到方法或者属性上。

    5、@Import使用方式

    使用方式:

    • 直接填写class数组的方式
    • ImportSelector,即批量导入(重点)
    • ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中

    5.1、验证@Import效果

    5.1.1、正常情况下没有@Import注解时的效果

    我们新建一个类叫White(空类),我们并未通过MainConfig进行注入,代码如下:

    package com.zhz.bean;
    
    /**
     * @author zhouhengzhe
     * @date 2022/11/15
     */
    public class White {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后我们运行下test方法,看看效果:
    在这里插入图片描述

    我们可以发现一个现状,他没有把White类注入到Spring容器中,也就是说没有这个Bean实例。

    5.2、使用方式详解

    5.2.1、直接填写class数组的方式

    5.2.1.1、Import注解类单导入

    然后我们简单使用下@Import,看一下演示效果,我们在MainConfig中添加一个@Import(White.class)去看一下效果,代码如下

    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import com.zhz.bean.White;
    import com.zhz.condition.AppleCondition;
    import com.zhz.condition.BananaCondition;
    import com.zhz.condition.WatermelonCondition;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/4 10:27
     * @since v1
     */
    @Configuration
    @Import(White.class)
    public class MainConfig {
    
        @Bean(name = "person")
        public Person person() {
            return new Person("apple", 20);
        }
    
        /**
         * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
         */
        @Bean(name = "apple")
        @Conditional(AppleCondition.class)
        public Person apple() {
            return new Person("apple", 20);
        }
    
        @Bean(name = "banana")
        @Conditional(BananaCondition.class)
        public Person banana() {
            return new Person("banana", 20);
        }
    
        @Bean(name = "watermelon")
        @Conditional(WatermelonCondition.class)
        public Person watermelon() {
            return new Person("watermelon", 20);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    然后我们运行一下test类,代码如下

      @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    运行效果如下:
    在这里插入图片描述

    • 我们可以发现通过@Import注解快速地导入组件时,容器中就会自动注册这个组件,并且id默认是组件的全类名。
    5.2.1.2、Import注解类批量导入

    我们在创建一个Bean类,来验证批量导入,代码如下:

    package com.zhz.bean;
    
    /**
     * @author zhouhengzhe
     * @date 2022/11/15
     */
    public class Blue {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在MainConfig中通过**@Import**注入,如下:

    package com.zhz.test;
    
    import com.zhz.bean.Person;
    import com.zhz.config.MainConfig;
    import com.zhz.scope.ThreadScope;
    import org.junit.Test;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/4 10:58
     * @since v1
     */
    public class IOCTest {
    
        @SuppressWarnings("resource")
        @Test
        public void test() {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            Person person = applicationContext.getBean(Person.class);
            System.out.println(person);
    //        Person person1 = applicationContext.getBean(Person.class);
    //        System.out.println(person == person1);
        }
    
        @Test
        public void test1() {
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
            // 向容器中注册自定义的Scope
            beanFactory.registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());
            for (int i = 0; i < 2; i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName() + ","+applicationContext.getBean("person"));
                    System.out.println(Thread.currentThread().getName() + ","+applicationContext.getBean("person"));
                }).start();
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        @Test
        public void test2(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            // 我们现在就来看一下IOC容器中Person这种类型的bean都有哪些
            String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
    
            for (String name : namesForType) {
                System.out.println(name);
            }
        }
    
        @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    我们写一个test方法,来看一下Spring容器中的类,代码如下:

      @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后我们看一下运行结果:
    在这里插入图片描述

    • 我们可以发现通过@Import注解,是可以同时导入多个Bean到Spring容器中。

    5.2.2、ImportSelector,即批量导入(重点)

    首先我们先看一下ImportSelector接口的源码,我们可以发现其是导入外部配置的核心接口,在SpringBoot的自动装配和@Enable(XXX)都会有用到这个接口。代码如下:

    /*
     * Copyright 2002-2020 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.annotation;
    
    import java.util.function.Predicate;
    
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.lang.Nullable;
    
    /**
     * Interface to be implemented by types that determine which @{@link Configuration}
     * class(es) should be imported based on a given selection criteria, usually one or
     * more annotation attributes.
     *
     * 

    An {@link ImportSelector} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, * and their respective methods will be called prior to {@link #selectImports}: *

      *
    • {@link org.springframework.context.EnvironmentAware EnvironmentAware}
    • *
    • {@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
    • *
    • {@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
    • *
    • {@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
    • *
    * *

    Alternatively, the class may provide a single constructor with one or more of * the following supported parameter types: *

      *
    • {@link org.springframework.core.env.Environment Environment}
    • *
    • {@link org.springframework.beans.factory.BeanFactory BeanFactory}
    • *
    • {@link java.lang.ClassLoader ClassLoader}
    • *
    • {@link org.springframework.core.io.ResourceLoader ResourceLoader}
    • *
    * *

    {@code ImportSelector} implementations are usually processed in the same way * as regular {@code @Import} annotations, however, it is also possible to defer * selection of imports until all {@code @Configuration} classes have been processed * (see {@link DeferredImportSelector} for details). * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see DeferredImportSelector * @see Import * @see ImportBeanDefinitionRegistrar * @see Configuration */ public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. * @return the class names, or an empty array if none */ String[] selectImports(AnnotationMetadata importingClassMetadata); /** * Return a predicate for excluding classes from the import candidates, to be * transitively applied to all classes found through this selector's imports. *

    If this predicate returns {@code true} for a given fully-qualified * class name, said class will not be considered as an imported configuration * class, bypassing class file loading as well as metadata introspection. * @return the filter predicate for fully-qualified candidate class names * of transitively imported configuration classes, or {@code null} if none * @since 5.2.4 * 返回用于从导入候选对象中排除类的谓词,为。 *可传递地应用于通过此选择器的导入找到的所有类。 *

    如果此谓词返回给定完全限定的。 *类名,所述类不会被视为导入的配置类,绕过类文件加载以及元数据自省。 *@返回 完全限定候选类名的筛选器谓词,在传递导入的配置类中, */ @Nullable default Predicate<String> getExclusionFilter() { return null; } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 主要作用是收集需要导入的配置类,selectImports()方法的返回值就是我们向Spring容器中导入的类的全类名。如果该接口的实现类同时实现EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports()方法之前先调用上述接口中对应的方法,如果需要在所有的@Configuration处理完再导入时,那么可以实现DeferredImportSelector接口。
    • 在ImportSelector接口的selectImports()方法中,存在一个AnnotationMetadata类型的参数,这个参数能够获取到当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
    5.2.2.1、上实践用例
    5.2.2.1.1、首先我们创建一个类它实现了ImportSelector接口,代码如下:
    package com.zhz.selecter;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * 自定义逻辑,返回需要导入的组件
     * @author zhouhengzhe
     * @date 2022/11/16
     */
    public class TestImportSelector implements ImportSelector {
    
        /**
         * Select and return the names of which class(es) should be imported based on
         * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
         *
         * @param importingClassMetadata 当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
         * @return 导入到容器中的组件的全类名
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return null;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    5.2.2.1.2、我们在MainConfig中通过@Import引入该类,代码如下:
    package com.zhz.config;
    
    import com.zhz.bean.Blue;
    import com.zhz.bean.Person;
    import com.zhz.bean.White;
    import com.zhz.condition.AppleCondition;
    import com.zhz.condition.BananaCondition;
    import com.zhz.condition.WatermelonCondition;
    import com.zhz.selecter.TestImportSelector;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/4 10:27
     * @since v1
     */
    
    @Configuration
    @Import(value = {White.class, Blue.class, TestImportSelector.class})
    public class MainConfig {
    
        @Bean(name = "person")
        public Person person() {
            return new Person("apple", 20);
        }
    
        /**
         * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
         */
        @Bean(name = "apple")
        @Conditional(AppleCondition.class)
        public Person apple() {
            return new Person("apple", 20);
        }
    
        @Bean(name = "banana")
        @Conditional(BananaCondition.class)
        public Person banana() {
            return new Person("banana", 20);
        }
    
        @Bean(name = "watermelon")
        @Conditional(WatermelonCondition.class)
        public Person watermelon() {
            return new Person("watermelon", 20);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    5.2.2.1.3、我们在TestImportSelector类上的selectImports()方法增加一个断点,然后我们dubug下面的测试方法

    在这里插入图片描述

     @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    5.2.2.1.4、我们dubug之后发现,打到这个断点之前,是没有任何输出的,说明是在bean实例化前就做了,如下

    在这里插入图片描述

    5.2.2.1.5、然后我们再回归selectImports()方法,看一下他的参数的值,可以发现他获取到了当前标注了@Import注解的类的所有注解信息。

    在这里插入图片描述

    我们发现执行到下一步的时候有个空指针异常,如下
    在这里插入图片描述

    控制台输出也是空指针异常。
    在这里插入图片描述

    5.2.2.1.6、我们来研究一下为什么会空指针?

    继续打开dubug,我们可以发现它后面执行到了,如下:
    在这里插入图片描述

    然后我们进入asSourceClasses()方法,然后它里面有一行拿了当前上一行的返回值,而上一行的返回值是null,那么null.length()自然而然就会空指针了。
    在这里插入图片描述

    5.2.2.1.7、防止以上空指针,代码改造

    我们把继承了ImportSelector的TestImportSelector类的selectImports()改造一下返回值,返回一个空数组,代码如下:
    在这里插入图片描述

    然后我们再运行下面的test方法,代码如下

      @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    运行结果为:
    在这里插入图片描述

    • 因为我们的selectImports()方法返回为一个空数组,所以自然而然就没有通过ImportSelector接口的方式注册的Bean的全类名集合输出了。
    5.2.2.1.8、改造White.class,blue.class的导入方式

    修改MainConfig,代码如下:

    package com.zhz.config;
    
    import com.zhz.bean.Blue;
    import com.zhz.bean.Person;
    import com.zhz.bean.White;
    import com.zhz.condition.AppleCondition;
    import com.zhz.condition.BananaCondition;
    import com.zhz.condition.WatermelonCondition;
    import com.zhz.selecter.TestImportSelector;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/4 10:27
     * @since v1
     */
    
    @Configuration
    @Import(value = {TestImportSelector.class})
    public class MainConfig {
    
        @Bean(name = "person")
        public Person person() {
            return new Person("apple", 20);
        }
        /**
         * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
         */
        @Bean(name = "apple")
        @Conditional(AppleCondition.class)
        public Person apple() {
            return new Person("apple", 20);
        }
    
        @Bean(name = "banana")
        @Conditional(BananaCondition.class)
        public Person banana() {
            return new Person("banana", 20);
        }
    
        @Bean(name = "watermelon")
        @Conditional(WatermelonCondition.class)
        public Person watermelon() {
            return new Person("watermelon", 20);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    然后我们改造一下TestImportSelector的selectImports()方法,代码如下:

    package com.zhz.selecter;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * 自定义逻辑,返回需要导入的组件
     * @author zhouhengzhe
     * @date 2022/11/16
     */
    public class TestImportSelector implements ImportSelector {
    
        /**
         * Select and return the names of which class(es) should be imported based on
         * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
         *
         * @param importingClassMetadata 当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
         * @return 导入到容器中的组件的全类名
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            /**
             * 1、方法不要返回null值,否则会报空指针异常
             * 2、可以返回一个空数组
             */
    
            return new String[]{"com.zhz.bean.Blue","com.zhz.bean.White"};
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    然后运行test方法,代码如下:

    
        @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                System.out.println(beanDefinitionName);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果如下:
    在这里插入图片描述

    • 因此,我们是不是可以发现Blue,White两个类也注入进来了

    5.2.3、ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中

    5.2.3.1、ImportBeanDefinitionRegistrar介绍

    首先我们先看其源码,代码如下

    /*
     * Copyright 2002-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.annotation;
    
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * Interface to be implemented by types that register additional bean definitions when
     * processing @{@link Configuration} classes. Useful when operating at the bean definition
     * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
     *
     * 

    Along with {@code @Configuration} and {@link ImportSelector}, classes of this type * may be provided to the @{@link Import} annotation (or may also be returned from an * {@code ImportSelector}). * *

    An {@link ImportBeanDefinitionRegistrar} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective * methods will be called prior to {@link #registerBeanDefinitions}: *

      *
    • {@link org.springframework.context.EnvironmentAware EnvironmentAware}
    • *
    • {@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware} *
    • {@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware} *
    • {@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware} *
    * *

    Alternatively, the class may provide a single constructor with one or more of * the following supported parameter types: *

      *
    • {@link org.springframework.core.env.Environment Environment}
    • *
    • {@link org.springframework.beans.factory.BeanFactory BeanFactory}
    • *
    • {@link java.lang.ClassLoader ClassLoader}
    • *
    • {@link org.springframework.core.io.ResourceLoader ResourceLoader}
    • *
    * *

    See implementations and associated unit tests for usage examples. * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see Import * @see ImportSelector * @see Configuration */ public interface ImportBeanDefinitionRegistrar { /** * Register bean definitions as necessary based on the given annotation metadata of * the importing {@code @Configuration} class. *

    Note that {@link BeanDefinitionRegistryPostProcessor} types may not be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. *

    The default implementation delegates to * {@link #registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)}. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry * @param importBeanNameGenerator the bean name generator strategy for imported beans: * {@link ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR} by default, or a * user-provided one if {@link ConfigurationClassPostProcessor#setBeanNameGenerator} * has been set. In the latter case, the passed-in strategy will be the same used for * component scanning in the containing application context (otherwise, the default * component-scan naming strategy is {@link AnnotationBeanNameGenerator#INSTANCE}). * @since 5.2 * @see ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR * @see ConfigurationClassPostProcessor#setBeanNameGenerator */ default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } /** * Register bean definitions as necessary based on the given annotation metadata of * the importing {@code @Configuration} class. *

    Note that {@link BeanDefinitionRegistryPostProcessor} types may not be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. *

    The default implementation is empty. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry * * 向Spring容器中注册bean实例 */ default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 由上可知:ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过该方法,我们可以向Spring容器中注册bean实例。
    • Spring在动态注册Bean的时候,都是采用这个ImportBeanDefinitionRegistrar接口,
    • 所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。
    5.2.3.2、使用方法

    ImportBeanDefinitionRegistrar需要配合@Configuration和@Import这俩注解,其中,@Configuration注解定义Java格式的Spring配置文件,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。

    5.2.3.3、ImportBeanDefinitionRegistrar接口实例

    1、创建一个ImportBeanDefinitionRegistrar的实现类,空实现方法registerBeanDefinitions()

    package com.zhz.registrar;
    
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * @author zhouhengzhe
     * @date 2022/11/16
     */
    public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        /**
         * AnnotationMetadata:当前类的注解信息
         * BeanDefinitionRegistry:BeanDefinition注册类
         * 

    * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2、接着我们在MainConfig中用@Import把他添加进去

    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import com.zhz.condition.AppleCondition;
    import com.zhz.condition.BananaCondition;
    import com.zhz.condition.WatermelonCondition;
    import com.zhz.registrar.TestImportBeanDefinitionRegistrar;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/4 10:27
     * @since v1
     */
    
    @Configuration
    @Import(value = {TestImportBeanDefinitionRegistrar.class})
    public class MainConfig {
    
        @Bean(name = "person")
        public Person person() {
            return new Person("apple", 20);
        }
    
        /**
         * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
         */
        @Bean(name = "apple")
        @Conditional(AppleCondition.class)
        public Person apple() {
            return new Person("apple", 20);
        }
    
        @Bean(name = "banana")
        @Conditional(BananaCondition.class)
        public Person banana() {
            return new Person("banana", 20);
        }
    
        @Bean(name = "watermelon")
        @Conditional(WatermelonCondition.class)
        public Person watermelon() {
            return new Person("watermelon", 20);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    3、然后我们运行下test方法,看一下效果
    在这里插入图片描述

    4、接着我们把registerBeanDefinitions()方法定制一下自己的逻辑

    package com.zhz.registrar;
    
    import com.zhz.bean.Blue;
    import com.zhz.bean.White;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * @author zhouhengzhe
     * @date 2022/11/16
     */
    public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        /**
         * AnnotationMetadata:当前类的注解信息
         * BeanDefinitionRegistry:BeanDefinition注册类
         * 

    * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinitionBlue = new RootBeanDefinition(Blue.class); RootBeanDefinition rootBeanDefinitionWhite = new RootBeanDefinition(White.class); registry.registerBeanDefinition("blue",rootBeanDefinitionBlue); registry.registerBeanDefinition("white",rootBeanDefinitionWhite); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    然后我们运行一下test方法,看一下效果
    在这里插入图片描述

    • 是不是发现了Blue,White被注入到Spring容器中了,并且命名还是我们自己命的,当然我们也可以让Spring自己生成,换一个父类方法就可以嘞,他注册的Bean名字是全类名
    package com.zhz.registrar;
    
    import com.zhz.bean.Blue;
    import com.zhz.bean.White;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * @author zhouhengzhe
     * @date 2022/11/16
     */
    public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        /**
         * AnnotationMetadata:当前类的注解信息
         * BeanDefinitionRegistry:BeanDefinition注册类
         * 

    * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean */ // @Override // public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // RootBeanDefinition rootBeanDefinitionBlue = new RootBeanDefinition(Blue.class); // RootBeanDefinition rootBeanDefinitionWhite = new RootBeanDefinition(White.class); // // registry.registerBeanDefinition("blue",rootBeanDefinitionBlue); // registry.registerBeanDefinition("white",rootBeanDefinitionWhite); // // } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { RootBeanDefinition rootBeanDefinitionBlue = new RootBeanDefinition(Blue.class); RootBeanDefinition rootBeanDefinitionWhite = new RootBeanDefinition(White.class); registry.registerBeanDefinition(importBeanNameGenerator.generateBeanName(rootBeanDefinitionBlue,registry),rootBeanDefinitionBlue); registry.registerBeanDefinition(importBeanNameGenerator.generateBeanName(rootBeanDefinitionWhite,registry),rootBeanDefinitionWhite); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述

  • 相关阅读:
    DeepMind爆发史:决定AI高峰的“游戏玩家”|深度学习崛起十年
    安装MyBatis教程
    2024年最新测评,6款好用的在线代码编辑器推荐
    R语言表与因子(详细知识点,深入知识点后续会补充!)
    Day44——MySQL表查询关键字
    从源码MessageSource的三个实现出发实战spring·i18n国际化
    云原生架构技术揭秘:DevOps 技术打破开发运维壁垒,实现持续交付的变革之道
    C#开发的OpenRA游戏之金钱系统(2)
    openfoam并行通信探索(一)
    PDF文件怎么转换成Word?这几种方法原来这么简单
  • 原文地址:https://blog.csdn.net/zhouhengzhe/article/details/127896419