• 【Spring】——6、按照条件向Spring容器中注册bean


    在这里插入图片描述

    📫作者简介: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),出行行业等有相关的开发领域
    🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

    当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,而不用再创建新的bean了。
    若bean是单实例,并且使用@Lazy注解设置了懒加载,则Spring容器启动时,不会立即实例化bean,自然就不会将bean注册到IOC容器中了,只有第一次获取bean的时候,才会实例化bean,并且将bean注册到IOC容器中。
    若bean是多实例,则Spring容器启动时,不会实例化bean,也不会将bean注册到IOC容器中,只是在以后每次从IOC容器中获取bean的时候,都会创建一个新的bean返回。
    其实,Spring支持按照条件向IOC容器中注册bean,满足条件的bean就会被注册到IOC容器中,不满足条件的bean就不会被注册到IOC容器中。

    @Conditional注解概述

    @Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。
    这个注解位于org.springframework.context.annotation包下,具体代码如下:

    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;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
    
    	/**
    	 * All {@link Condition} classes that must {@linkplain Condition#matches match}
    	 * in order for the component to be registered.
    	 */
    	Class<? extends Condition>[] value();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    从源码上来说,这个注解可以用于类上和方法上。并且我们还可以发现他有个方法的返回值是泛型上限为Condition,我们可以来看一下这个Condition类,代码如下:

    package org.springframework.context.annotation;
    
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    @FunctionalInterface
    public interface Condition {
    
    	/**
    	 * Determine if the condition matches.
    	 * @param context the condition context
    	 * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
    	 * or {@link org.springframework.core.type.MethodMetadata method} being checked
    	 * @return {@code true} if the condition matches and the component can be registered,
    	 * or {@code false} to veto the annotated component's registration
    	 */
    	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我们可以发现Condition是一个函数式接口,当我们需要使用@Condition直接时,他是需要一个类来实现Spring提供的Condition接口,它里面的matches会根据规则去匹配对应的方法,然后我们就可以用@Conditional注解中定义的类,就是@Condition括号里面的类。
    那么我们说了这么久,究竟@Condition能用于什么场景呢?
    在这里插入图片描述

    向Spring容器注册bean

    不带条件注册bean

    让我来看一下正常情况下SpringBean容器里面的情况,我们在MainConfig中添加几个类,Apple,Banana,Watermelon。由于类太多的话也麻烦,所以我这里就给每个person建一个这样的名字,代表Apple,Banana,Watermelon。

    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import com.zhz.filter.MyTypeFilter;
    import org.springframework.context.annotation.*;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/4 10:27
     * @since v1
     */
    
    @Configuration
    public class MainConfig {
    
        /**
         * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
         */
        @Bean(name = "apple")
        public Person apple() {
            return new Person("apple", 20);
        }
    
        @Bean(name = "banana")
        public Person banana() {
            return new Person("banana", 20);
        }
    
        @Bean(name = "watermelon")
        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

    测试类,我们把Spring容器中属于我们的bean打印出来

    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 org.springframework.context.support.ClassPathXmlApplicationContext;
    
    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);
            }
        }
    }
    
    
    • 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

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

    带条件注册bean

    假设我们现在要在上面(不带条件注册的Bean)的样例之外,增加一个新的需求,叫做当我们当前的一个属性是apple,那他就是Apple,同理是banana就是Banana,以此类推,那么我们怎么实现呢?接下来我们就要用到我们的@Condition注解了。
    我们先创建对应的三个类的Condition类,如下

    package com.zhz.condition;
    
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    import java.util.Objects;
    
    /**
     * @author zhouhengzhe
     * @description: 判断水果是否是苹果
     * @date 2022/11/8 2:09
     * @since v1
     */
    public class AppleCondition implements Condition {
    
        /**
         * ConditionContext:判断条件能使用的上下文(环境)
         * AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            // 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            System.out.println(beanFactory);
            // 2. 获取到类加载器
            ClassLoader classLoader = context.getClassLoader();
            System.out.println(classLoader);
            // 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
            Environment environment = context.getEnvironment();
            // 4. 获取到bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
            System.out.println(registry);
            String property = environment.getProperty("spring.fruit.name");
            if (Objects.nonNull(property)){
                if (property.contains("apple")){
                    return true;
                }
            }
    
            return false;
        }
    }
    
    • 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
    package com.zhz.condition;
    
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    import java.util.Objects;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/8 2:09
     * @since v1
     */
    public class BananaCondition implements Condition {
    
        /**
         * ConditionContext:判断条件能使用的上下文(环境)
         * AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            // 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            System.out.println(beanFactory);
            // 2. 获取到类加载器
            ClassLoader classLoader = context.getClassLoader();
            System.out.println(classLoader);
            // 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
            Environment environment = context.getEnvironment();
            // 4. 获取到bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
            System.out.println(registry);
            String property = environment.getProperty("spring.fruit.name");
            if (Objects.nonNull(property)) {
                if (property.contains("banana")) {
                    return true;
                }
            }
            return false;
        }
    }
    
    • 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
    package com.zhz.condition;
    
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    import java.util.Objects;
    
    /**
     * @author zhouhengzhe
     * @description: todo
     * @date 2022/11/8 2:09
     * @since v1
     */
    public class WatermelonCondition implements Condition {
    
        /**
         * ConditionContext:判断条件能使用的上下文(环境)
         * AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            // 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            System.out.println(beanFactory);
            // 2. 获取到类加载器
            ClassLoader classLoader = context.getClassLoader();
            System.out.println(classLoader);
            // 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
            Environment environment = context.getEnvironment();
            // 4. 获取到bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
            System.out.println(registry);
            String property = environment.getProperty("spring.fruit.name");
            if (Objects.nonNull(property)) {
                if (property.contains("watermelon")) {
                    return true;
                }
            }
            return false;
        }
    }
    
    • 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

    接下来我们启动下测试类,但是启动之前需要配置一下参数,我们添加一个**-Dspring.fruit.name=apple**
    在这里插入图片描述

    然后我们启动测试类。

    @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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    扩展点

    BeanDefinitionRegistry

    • spring中所有的Bean都是通过BeanDefinitionRegistry对象来进行注册的,因此我们可以通过它来查看Spring容器中注册了哪些bean
    registerBeanDefinition
    • 该方法表明我们可以通过BeanDefinitionRegistry对象向Spring容器中注册一个Bean
    removeBeanDefinition
    • 该方法表明我们可以通过BeanDefinitionRegistry对象向Spring容器中注移除一个Bean
    getBeanDefinition
    • 该方法表明我们可以通过BeanDefinitionRegistry对象查看某个Bean的定义信息
    containsBeanDefinition
    • 该方法表明我们可以通过BeanDefinitionRegistry对象查看Spring容器中是否包含某一个Bean的定义
    getBeanDefinitionNames
    • 获取Bean的所有名字
    getBeanDefinitionCount
    • 获取Bean的所有数量
    isBeanNameInUse
    • 获取正在使用中的Bean

    @Conditional的扩展注解

    在这里插入图片描述

    @Conditional与@Profile这俩注解的对比

    Spring 3.0也有一些和@Conditional相似的注解,它们是Spring SPEL表达式和Spring Profiles注解,但是Spring 4.0之后的@Conditional注解要比@Profile注解更加高级。@Profile注解用来加载应用程序的环境,该注解仅限于根据预定义属性编写条件检查,而@Conditional注解则没有此限制。
    Spring中的@Profile和@Conditional这俩注解都是用来检查If…then…else的语义。然而,Spring 4.0之后的@Conditional注解是@Profile注解的更新用法。

    • Spring 3.0中的@Profile仅用于编写基于Environment变量的条件检查。配置文件可用于基于环境加载应用程序配置的场景。
    • Spring 4.0之后的@Conditional注解允许开发人员为条件检查定义用户定义的策略。此外,@Conditional注解还可以用于条件bean注册。
  • 相关阅读:
    运维-技能大杂烩
    [Asp.Net Core]Asp.Net Core与配置系统的集成
    跟着医生学php:为什么tr的间距无法设定?
    [项目管理-30]:项目成员成熟度以及采取的不同的策略
    HTML---表单验证
    国产浏览器的创新之路,拼的就是用户体验
    不用445端口,替代Windows共享,局域网文件共享新方式
    连接表(二)
    [附源码]Python计算机毕业设计Django拉勾教育课程管理系统
    C#:VARCHART XGantt 5.2.0.167-2022-08-18-UPDATE
  • 原文地址:https://blog.csdn.net/zhouhengzhe/article/details/127896400