【Spring Boot】 SpringBoot自动装配-Condition

news2024/9/21 19:56:48

目录

    • 一、前言
    • 二、 定义
      • 2.1 @Conditional
      • 2.2 Condition
      • 2.2.1 ConditionContext
    • 三、 使用说明
      • 3.1 创建项目
        • 3.1.1 导入依赖
        • 3.1.2 添加配置信息
        • 3.1.3 创建User类
        • 3.1.4 创建条件实现类
        • 3.1.5 修改启动类
      • 3.2 测试
        • 3.2.1 当user.enable=false
        • 3.2.2 当user.enable=true
      • 3.3 小结
    • 四、改进
      • 4.1 创建注解
      • 4.2 修改UserCondition
    • 五、 Spring内置条件注解

在这里插入图片描述
在这里插入图片描述

一、前言

@Conditional注解在Spring4.0中引入,其主要作用就是判断条件是否满足,从而决定是否初始化并向容器注册Bean。

二、 定义

2.1 @Conditional

@Conditional注解定义如下:其内部只有一个参数为Class对象数组,且必须继承自Condition接口,通过重写Condition接口的matches方法来判断是否需要加载Bean

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
  Class<? extends Condition>[] value();
}

2.2 Condition

Condition接口定义如下:该接口为一个函数式接口,只有一个matches接口,形参为ConditionContext context, AnnotatedTypeMetadata metadata。ConditionContext定义如2.2.1,AnnotatedTypeMetadata见名知意,就是用来获取注解的元信息的

@FunctionalInterface
public interface Condition {
  boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

2.2.1 ConditionContext

ConditionContext接口定义如下:通过查看源码可以知道,从这个类中可以获取很多有用的信息

public interface ConditionContext {
  /**
   * 返回Bean定义信息
   * Return the {@link BeanDefinitionRegistry} that will hold the bean definition
   * should the condition match.
   * @throws IllegalStateException if no registry is available (which is unusual:
   * only the case with a plain {@link ClassPathScanningCandidateComponentProvider})
   */
  BeanDefinitionRegistry getRegistry();

  /**
   * 返回Bean工厂
   * Return the {@link ConfigurableListableBeanFactory} that will hold the bean
   * definition should the condition match, or {@code null} if the bean factory is
   * not available (or not downcastable to {@code ConfigurableListableBeanFactory}).
   */
  @Nullable
  ConfigurableListableBeanFactory getBeanFactory();

  /**
   * 返回环境变量 比如在application.yaml中定义的信息
   * Return the {@link Environment} for which the current application is running.
   */
  Environment getEnvironment();

  /**
   * 返回资源加载器
   * Return the {@link ResourceLoader} currently being used.
   */
  ResourceLoader getResourceLoader();

  /**
   * 返回类加载器
   * Return the {@link ClassLoader} that should be used to load additional classes
   * (only {@code null} if even the system ClassLoader isn't accessible).
   * @see org.springframework.util.ClassUtils#forName(String, ClassLoader)
   */
  @Nullable
  ClassLoader getClassLoader();
}

三、 使用说明

通过一个简单的小例子测试一下@Conditional是不是真的能实现Bean的条件化注入。

3.1 创建项目

在这里插入图片描述

首先我们创建一个SpringBoot项目

3.1.1 导入依赖

这里我们除了springboot依赖,再添加个lombok依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ldx</groupId>
    <artifactId>condition</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>condition</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
3.1.2 添加配置信息

在application.yaml 中加入配置信息

user:
  enable: false
3.1.3 创建User类
package com.ldx.condition;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * 用户信息
 * @author ludangxin
 * @date 2021/8/1
 */
@Data
@AllArgsConstructor
public class User {
   private String name;
   private Integer age;
}
3.1.4 创建条件实现类
package com.ldx.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * 用户bean条件判断
 * @author ludangxin
 * @date 2021/8/1
 */
public class UserCondition implements Condition {
   @Override
   public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
      Environment environment = conditionContext.getEnvironment();
      // 获取property user.enable
      String property = environment.getProperty("user.enable");
      // 如果user.enable的值等于true 那么返回值为true,反之为false
      return "true".equals(property);
   }
}
3.1.5 修改启动类
package com.ldx.condition;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;

@Slf4j
@SpringBootApplication
public class ConditionApplication {

   public static void main(String[] args) {
      ConfigurableApplicationContext applicationContext = SpringApplication.run(ConditionApplication.class, args);
      // 获取类型为User类的Bean
      User user = applicationContext.getBean(User.class);
      log.info("user bean === {}", user);
   }

  /**
   * 注入User类型的Bean
   */
   @Bean
   @Conditional(UserCondition.class)
   public User getUser(){
      return new User("张三",18);
   }

}

3.2 测试

3.2.1 当user.enable=false

报错找不到可用的User类型的Bean

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.3)


Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ldx.condition.User' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172)
	at com.ldx.condition.ConditionApplication.main(ConditionApplication.java:16)

Process finished with exit code 1
3.2.2 当user.enable=true

正常输出UserBean实例信息

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.3)

com.ldx.condition.ConditionApplication   : user bean === User(name=张三, age=18)

3.3 小结

上面的例子通过使用@Conditional和Condition接口,实现了spring bean的条件化注入。

好处:

  1. 可以实现某些配置的开关功能,如上面的例子,我们可以将UserBean换成开启缓存的配置,当property的值为true时,我们才开启缓存的配置

  2. 当有多个同名的bean时,如何抉择的问题。

  3. 实现自动化的装载。如判断当前classpath中有mysql的驱动类时(说明我们当前的系统需要使用mysql),我们就自动的读取application.yaml中的mysql配置,实现自动装载;当没有驱动时,就不加载。

四、改进

从上面的使用说明中我们了解到了条件注解的大概使用方法,但是代码中还是有很多硬编码的问题。比如:UserCondition中的property的key包括value都是硬编码,其实我们可以通过再扩展一个注解来实现动态的判断和绑定。

4.1 创建注解

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;

/**
 * 自定义条件属性注解
 * <p>
 *   当配置的property name对应的值 与设置的 value值相等时,则注入bean
 * @author ludangxin
 * @date 2021/8/1
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 指定condition的实现类
@Conditional({UserCondition.class})
public @interface MyConditionOnProperty {
   // 配置信息的key
   String name();
   // 配置信息key对应的值
   String value();
}

4.2 修改UserCondition

package com.ldx.condition;

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.Map;

/**
 * 用户bean条件判断
 * @author ludangxin
 * @date 2021/8/1
 */
public class UserCondition implements Condition {
   @Override
   public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
      Environment environment = conditionContext.getEnvironment();
      // 获取自定义的注解
      Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes("com.ldx.condition.MyConditionOnProperty");
      // 获取在注解中指定的name的property的值 如:user.enable的值
      String property = environment.getProperty(annotationAttributes.get("name").toString());
      // 获取预期的值
      String value = annotationAttributes.get("value").toString();
      return value.equals(property);
   }
}

测试后,结果符合预期。

其实在spring中已经内置了许多常用的条件注解,其中我们刚实现的就在内置的注解中已经实现了,如下。

五、 Spring内置条件注解

注解 说明

  • @ConditionalOnSingleCandidate 当给定类型的bean存在并且指定为Primary的给定类型存在时,返回true
  • @ConditionalOnMissingBean 当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true.各类型间是or的关系
  • @ConditionalOnBean 与上面相反,要求bean存
  • @ConditionalOnMissingClass 当给定的类名在类路径上不存在时返回true,各类型间是and的关系
  • @ConditionalOnClass 与上面相反,要求类存在
  • @ConditionalOnCloudPlatform 当所配置的CloudPlatform为激活时返回true
  • @ConditionalOnExpression spel表达式执行为true
  • @ConditionalOnJava 运行时的java版本号是否包含给定的版本号.如果包含,返回匹配,否则,返回不匹配
  • @ConditionalOnProperty 要求配置属性匹配条件 @ConditionalOnJndi 给定的jndi的Location必须存在一个.否则,返回不匹配
  • @ConditionalOnNotWebApplication web环境不存在时
  • @ConditionalOnWebApplication web环境存在时
  • @ConditionalOnResource 要求制定的资源存在

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

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

相关文章

如何实现加密功能

文章目录 1. 概念介绍2. 方法与功能2.1 基本用法2.2 加密算法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"FlutterCacheManager组件"相关的内容&#xff0c;本章回中将介绍一个加密工具包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 加密主要…

基于YOLO8的图片实例分割系统

文章目录 在线体验快速开始一、项目介绍篇1.1 YOLO81.2 ultralytics1.3 模块介绍1.3.1 scan_task1.3.2 scan_taskflow.py1.3.3 segment_app.py 二、核心代码介绍篇2.1 segment_app.py2.2 scan_taskflow.py 三、结语 代码资源&#xff1a;计算机视觉领域YOLO8技术的图片实例分割…

0x05 tomcat AJP文件包含漏洞(CVE-2020-1938)复现(脚本最终没有验证成功)

参考&#xff1a; 13-3 tomcat AJP文件包含漏洞&#xff08;CVE-2020-1938&#xff09;_omcat ajp文件包含漏洞 payload-CSDN博客 一、fofa 搜索使用该服务器的网站 网络空间测绘&#xff0c;网络空间安全搜索引擎&#xff0c;网络空间搜索引擎&#xff0c;安全态势感知 - F…

linux编译器——gcc/g++

1.gcc linux上先要安装&#xff0c; sudo yum install gcc gcc --version 可以查看当前的版本 &#xff0c;我们默认安装的是4.8.5的版本&#xff0c;比较低&#xff0c; gcc test.c -stdc99 可以使他支持更高版本的c标准 -o 可以殖指明生成文件的名字&#xff0c;可以自己…

什么是Web服务器集群?

Web服务器集群是指将多台服务器组成一个集群&#xff0c;通过负载均衡将客户端请求分发到这些服务器上进行处理&#xff0c;从而提高网站的性能和可用性。每台服务器都运行着相同的应用程序和数据&#xff0c;并且能够相互通信和协调工作。 1.为什么需要Web服务器集群 随着互联…

0基础学习爬虫系列:网页内容爬取

1.背景 今天我们来实现&#xff0c;监控网站最新数据爬虫。 在信息爆炸的年代&#xff0c;能够有一个爬虫帮你&#xff0c;将你感兴趣的最新消息推送给你&#xff0c;能够帮你节约非常多时间&#xff0c;同时确保不会miss重要信息。 爬虫应用场景&#xff1a; 应用场景主要功…

Transformer从零详细解读

Transformer从零详细解读 一、从全局角度概况Transformer ​ 我们把TRM想象为一个黑盒&#xff0c;我们的任务是一个翻译任务&#xff0c;那么我们的输入是中文的“我爱你”&#xff0c;输入经过TRM得到的结果为英文的“I LOVE YOU” ​ 接下来我们对TRM进行细化&#xff0c;…

【Linux】萌新看过来!一篇文章带你走进Linux世界

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言&#x1f4a5;1、初识Linux&#x1f4a5;1.1 什么是操作系统&#xff1f;&#x1f4a5;1.2 各种操作…

分享一个基于微信小程序的医院挂号就诊一体化平台uniapp医院辅助挂号应用小程序设计(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

SpringBoot学习(9)(springboot自动配置原理)(源码分析、面试题)

目录 一、引言 二、为啥学习自动配置原理&#xff1f; 三、自动配置 &#xff08;1&#xff09;基本概述 &#xff08;2&#xff09;学习回顾 四、自动配置——源码分析 &#xff08;1&#xff09;回顾学习 &#xff08;2&#xff09;回到源码学习 &#xff08;1&#xff09;注…

文件系统 文件描述符fd 重定向原理 缓冲区

文章目录 基础的文件操作文件的系统调用接口位图向文件中写入标记位选项总结&#xff1a;open的返回值文件描述符fdfd012与硬件的关系read && stat 重定向dup2 缓冲区的理解经典的例子 基础的文件操作 引子&#xff1a; #include <stdio.h>int main() {FILE* f…

[Linux]:环境变量与进程地址空间

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. 环境变量 1.1 概念 **环境变量(environment variables)**一般是指在操作…

在Unity环境中使用UTF-8编码

为什么要讨论这个问题 为了避免乱码和更好的跨平台 我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本…

自己部门日均1000+告警?如何减少90%无效告警?

目录标题 一、告警的类别1.技术告警1.1基础设施告警1.2基本服务告警 2.业务告警3.监控大盘告警 二、为何需要告警治理&#xff1f;三、治理迫在眉睫1.1告警治理策略1.2核心监控告警点1.3避免告警反模式1.4告警规约制定1.5自动化处理 一、告警的类别 一般的告警分为以下几点&am…

ISP面试准备2

系列文章目录 文章目录 系列文章目录前言一.如何评价图像质量&#xff1f;二.引起图像噪声的原因三. ISP3.1 ISP Pipeline主要模块3.1.1坏点校正&#xff08;Defect Pixel Correction, DPC&#xff09;3.1.2黑电平校正&#xff08;Black Level Correction, BLC&#xff09;3.1.…

面试官:synchronized的锁升级过程是怎样的?

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 回答 在 JDK 1.6之前&#xff0c;synchronized 是一个重量级、效率比较低下的锁&#xff0c;但是在JDK 1.6后&#xff0c;JVM 为了提高锁的获取与释放效&#xff0c;,对 synchronized 进…

基于JSP的实验室管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 Spring Boot框架 工具&#xff1a;IDEA/Eclipse、Navicat、Tomcat 系统展示 首页 用户个…

自然语言处理系列六十二》神经网络算法》MLP多层感知机算法

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列六十二神经网络算法》MLP多层感知机算法CNN卷积…

【Python篇】PyQt5 超详细教程——由入门到精通(序篇)

文章目录 PyQt5 超详细入门级教程前言序篇&#xff1a;1-3部分&#xff1a;PyQt5基础与常用控件第1部分&#xff1a;初识 PyQt5 和安装1.1 什么是 PyQt5&#xff1f;1.2 在 PyCharm 中安装 PyQt51.3 在 PyCharm 中编写第一个 PyQt5 应用程序1.4 代码详细解释1.5 在 PyCharm 中运…

电子电气架构---私有总线通信和诊断规则

电子电气架构—私有总线通信和诊断规则 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自…