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

news2024/11/18 21:34:10

在这里插入图片描述

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

文章目录

  • @Conditional注解概述
  • 向Spring容器注册bean
    • 不带条件注册bean
    • 带条件注册bean
    • 扩展点
      • BeanDefinitionRegistry
        • registerBeanDefinition
        • removeBeanDefinition
        • getBeanDefinition
        • containsBeanDefinition
        • getBeanDefinitionNames
        • getBeanDefinitionCount
        • isBeanNameInUse
  • @Conditional的扩展注解
  • @Conditional与@Profile这俩注解的对比

当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();

}

从源码上来说,这个注解可以用于类上和方法上。并且我们还可以发现他有个方法的返回值是泛型上限为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);
}

我们可以发现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);
    }
}

测试类,我们把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);
        }
    }
}

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

带条件注册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;
    }
}
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;
    }
}
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;
    }
}

接下来我们启动下测试类,但是启动之前需要配置一下参数,我们添加一个**-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);
        }
    }

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

扩展点

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注册。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

静态时序分析简明教程(六)]时钟组与其他时钟特性

生成时钟的sdc约束方法一、写在前面1.1 快速导航链接二、时钟组2.1 引入时钟组2.2 set_clock_group2.2.1 -name2.2.2 -group clock_list2.2.3 -logically_exclusive|-physically_exclusive|-asynchronous2.2.4 -allow_path2.2.5 -comment三、其他时钟特性3.1 过渡时间3.2 偏移与…

【Linux】进程间通信——管道

目录 一、概念 二、管道函数 1.popen函数 2.pclose函数 3.文件函数 三、管道的操作 1.管道的分类 无名管道 有名管道 管道的特点 四、管道的实现 操作系统对进程之间相互保护 两个进程之间相互通信 前言&#xff1a; 进程间通信的方法/IPC机制都有哪些&#xff1a; …

求二进制中1的个数的三种方法

求二进制中的1的个数 文章目录第一种方法&#xff1a;模2除2第二种方法&#xff1a;利用操作符右移后与1第三种方法&#xff1a;该数与上比它小1的数&#xff08;最优的方法&#xff09;第一种方法&#xff1a;模2除2 首先明白如何得到一个数的十进制的每一位&#xff1f; 以1…

PHP代码审计入门-DVWA靶场CSRF篇

0x00 写在前面 从零学习php&#xff0c;最终目的实现代码审计入门&#xff0c;软件采用sublime text&#xff0c;环境使用phpstudy搭建&#xff0c;数据库是navicat&#xff0c;需要有基本的前端基础、简单的phpmysql后端基础、渗透知识和漏洞原理&#xff0c;文章跟随流沙前…

bizlog通用操作日志组件(使用篇)

引言 如上图所示&#xff0c;产品的新需求&#xff0c;需要将操作人在系统中具体编辑操作的变更内容记录下来。 按正常思路来说&#xff0c;无非就是将修改前后的对象字段逐个比较&#xff0c;再拼接为详细的操作描述记录到操作日志表中。如果是一个模块的需求&#xff0c;单独…

用HTML+CSS做一个学生抗疫感动专题网页设计作业网页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢?

转自: springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢? 下文讲述springboot修改thymefleaf修改页面默认路径及后缀名的方法分享&#xff0c;如下所示: 实现思路:只需在配置文件中修改相应的配置即可&#xff0c;如&#xff1a;application.yaml spring:thym…

MySQL单表查询操作详解,不做CRUD程序员

在我们对数据进行操作时&#xff0c;查询无疑是至关重要的&#xff0c;查询操作灵活多变&#xff0c;我们可以根据开发的需求&#xff0c;设计高效的查询操作&#xff0c;把数据库中存储的数据展示给用户。 文章目录前言1. 基础查询1.1 基础查询语法1.2 基础查询练习2. 条件查询…

大数据路线

一、概念部分 1.1 大数据、数仓、数据湖、中台的概念 区别数仓数据湖使用场景批处理&#xff0c;BI&#xff0c;数据可视化机器学习、预测分析、数据分析Schema写入型读取型数据源类型OLTP为主的结构化数据loT&#xff0c;日志&#xff0c;各个端等结构非结构均可性价比需要快…

牛客刷题总结——Python入门08:面向对象、正则表达式

&#x1f935;‍♂️ 个人主页: 北极的三哈 个人主页 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;Python领域优质创作者。 &#x1f4d2; 系列专栏&#xff1a;《牛客题库-Python篇》 &#x1f310;推荐《牛客网》——找工作神器|笔试题库|面试经验|实习经验内推&am…

Design A Youtube

title: Notes of System Design No.05 — Design a Youtube description: ‘Design a Youtube’ date: 2022-05-14 13:45:37 tags: 系统设计 categories: 系统设计 01. Funtional Requirements 02. Non Functional Requirements 03.Assumption 04 API 05 High Level Design 上…

05 MSYS2中安装树莓派32位和64位ARM交叉编译工具

作者将狼才鲸创建日期2022-11-14 Gitee源码和工程地址&#xff1a;才鲸嵌入式 / 开源安防摄像机&#xff08;嵌入式软件&#xff09;CSDN文章地址&#xff1a;项目介绍&#xff1a;开源安防摄像机&#xff08;嵌入式软件&#xff09; 4.3 MSYS2中安装32位和64位ARM交叉编译工具…

1524_AURIX TC275存储分布_下

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 继续前面的学习&#xff0c;这一次把这个小章节的剩余信息看完。 这一部分是外设相关的寄存器地址区间描述&#xff0c;看起来一个模块的地址空间占用基本都是256个字节。具体包括什么暂时…

Unity技术手册-UGUI零基础详细教程-Graphic Raycaster 射线检测和Canvas Group

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 本文约3千字&#xff0c;新手阅读需要6分钟&#xff0c;复习需要2分钟 【收藏随时查阅不再迷路】 &#x1f449;关于作者 众所周知&#…

outsystems合集系列(三)

outsystemsModeling DataDatabase Entities的介绍如何创建Database Entities如何用excel快速导入真实数据到entity?如何用excel快速创建entity并导入真实数据?Static Entities的介绍Modeling Data 这一节我将介绍在outsystems中建模数据(model data)的一些思路。注意在这里我…

shellcode 中 null byte 的成因和避免方式总结

背景 shellcode 中要避免 null byte&#xff08;\x00&#xff09;这个是个通用的概念&#xff08;windows&#xff0c;linux 都是一样&#xff09;&#xff0c;因为栈溢出的数据作为字符串写入到栈上&#xff0c;\x00 会作为字符串终止符&#xff0c;毁掉整个 shellcode。 这…

HTML+CSS个人静态网页设计

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【AGC】安全规则resource.data获取不到字段

问题背景 在表结构里有但resource.data获取不到uid字段 解决该问题 request变量是指请求&#xff0c;request.resource.data是请求数据的所有字段和值的映射&#xff1b;resource变量是指所请求的数据在数据库中保存的状态&#xff0c;resource.data是数据库中保存数据的所有…

教师工作量管理系统思路(链表应用)

教师工作量管理系统思路&#xff08;链表应用&#xff09; 文章目录教师工作量管理系统思路&#xff08;链表应用&#xff09;题目描述&#xff1a;初始信息菜单部分数据结构功能实现查询历史信息从键盘录入信息信息删除和修改工作量计算如何存储到工作量信息链表中&#xff1f…

Bootstrap响应式轮播效果网页(1+X Web前端开发中级 例题)

文章目录 &#x1f4c4;题目要求 &#x1f9e9;说明 &#x1f9e9;效果图 &#x1f4bb;HTML代码 &#x1f3af;实现效果 &#x1f4f0;完整答案 &#x1f4c4;题目要求 阅读下列说明、效果图和HTML代码&#xff0c;进行静态网页开发&#xff0c;填写&#xff08;1&…