Spring Boot中的那些条件判断的实现方法
作者:沈子平 发布时间:2023-04-26 15:02:07
Spring Boot中的那些Conditional
spring boot中为我们提供了丰富的Conditional来让我们得以非常方便的在项目中向容器中添加Bean。本文主要是对各个注解进行解释并辅以代码说明其用途。
所有ConditionalOnXXX的注解都可以放置在class或是method上,如果方式在class上,则会决定该class中所有的@Bean注解方法是否执行。
@Conditional
下面其他的Conditional注解均是语法糖,可以通过下面的方法自定义ConditionalOnXXX
Conditional注解定义如下,接收实现Condition接口的class数组。
public @interface Conditional {
Class<? extends Condition>[] value();
}
而Condition接口只有一个matchs方法,返回是否匹配的结果。
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
通过操作系统进行条件判断,从而进行Bean配置。当Window时,实例化Bill的Person对象,当Linux时,实例化Linus的Person对象。
//LinuxCondition,为方便起见,去掉判断代码,直接返回true了
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return true;
}
}
//WindowsCondition,为方便起见,去掉判断代码,直接返回false了
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
return false;
}
}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
}
//配置类
@Configuration
public class BeanConfig {
@Bean(name = "bill")
@Conditional({WindowsCondition.class})
public Person person1(){
return new Person("Bill Gates",62);
}
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person person2(){
return new Person("Linus",48);
}
}
public class AppTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test(){
String osName = applicationContext.getEnvironment().getProperty("os.name");
System.out.println("当前系统为:" + osName);
Map<String, Person> map = applicationContext.getBeansOfType(Person.class);
System.out.println(map);
}
}
输出的结果:
当前系统为:Mac OS X
{linus=Person(name=Linus, age=48)}
@ConditionalOnBean & @ConditionalOnMissingBean
这两个注解会对Bean容器中的Bean对象进行判断,使用的例子是配置的时候,如果发现如果没有Computer实例,则实例化一个备用电脑。
@Data
@AllArgsConstructor
@ToString
public class Computer {
private String name;
}
@Configuration
public class BeanConfig {
@Bean(name = "notebookPC")
public Computer computer1(){
return new Computer("笔记本电脑");
}
@ConditionalOnMissingBean(Computer.class)
@Bean("reservePC")
public Computer computer2(){
return new Computer("备用电脑");
}
}
public class TestApp {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test1(){
Map<String,Computer> map = applicationContext.getBeansOfType(Computer.class);
System.out.println(map);
}
}
修改BeanConfig,如果注释掉第一个@Bean,会实例化备用电脑,否则就不会实例化备用电脑
@ConditionalOnClass & @ConditionalOnMissingClass
这个注解会判断类路径上是否有指定的类,一开始看到的时候比较困惑,类路径上如果没有指定的class,那编译也通过不了啊...这个主要用于集成相同功能的第三方组件时用,只要类路径上有该组件的类,就进行自动配置,比如spring boot web在自动配置视图组件时,是用Velocity,还是Thymeleaf,或是freemaker时,使用的就是这种方式。
例子是两套盔甲A(光明套装)和B(暗黑套装),如果A不在则配置B。
public interface Fighter {
void fight();
}
public class FighterA implements Fighter {
@Override
public void fight() {
System.out.println("使用光明套装");
}
}
public class FighterB implements Fighter {
@Override
public void fight() {
System.out.println("使用暗黑套装");
}
}
Van是武士,使用套装进行战斗
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Van {
private Fighter fighter;
public void fight(){
fighter.fight();
}
}
VanConfigA/B实例化武士
@Configuration
@ConditionalOnClass({FighterA.class})
public class VanConfigA {
@Primary
@Bean
public Van vanA(){
return new Van(new FighterA());
}
}
@Configuration
@ConditionalOnClass({FighterB.class})
public class VanConfigB {
@Bean
public Van vanB(){
return new Van(new FighterB());
}
}
测试类,默认情况,如果套装AB都在类路径上,两套都会加载,A会设置为PRIMARY,如果在target class中将FightA.class删除,则只会加载套装B。
@SpringBootApplication
public class TestApp implements CommandLineRunner {
@Autowired
private Van van;
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
@Override
public void run(String... args) throws Exception {
//do something
van.fight();
}
}
另外,尝试将两个VanConfigA/B合并,将注解ConditionalOnClass放到方法上,如果删除一个套装就会运行出错。
@ConditionalOnExpress
依据表达式进行条件判断,这个作用和@ConditionalOnProperty大部分情况可以通用,表达式更灵活一点,因为可以使用SpEL。例子中会判断properties中test.enabled的值进行判断。BeanConfig分别对布尔,字符串和数字三种类型进行判断。数字尝试了很多其他的方式均不行,比如直接使用==,貌似配置的属性都会当成字符串来处理。
@Data
public class TestBean {
private String name;
}
@Configuration
@ConditionalOnExpression("#{${test.enabled:true} }")
//@ConditionalOnExpression("'zz'.equalsIgnoreCase('${test.name2}')")
//@ConditionalOnExpression("new Integer('${test.account}')==1")
public class BeanConfig {
@Bean
public TestBean testBean(){
return new TestBean("我是美猴王");
}
}
@SpringBootApplication
public class TestAppCommand implements CommandLineRunner {
@Autowired
private TestBean testBean;
public static void main(String[] args) {
SpringApplication.run(TestAppCommand.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println(testBean.getName());
}
}
@ConditionalOnProperty
适合对单个Property进行条件判断,而上面的@ConditionalOnExpress适合面对较为复杂的情况,比如多个property的关联比较。这个例子也给了三种基本类型的条件判断,不过貌似均当成字符串就可以...
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestBean {
private String name;
}
@Configuration
@ConditionalOnProperty(prefix = "test", name="enabled", havingValue = "true",matchIfMissing = false)
//@ConditionalOnProperty(prefix = "test", name="account", havingValue = "1",matchIfMissing = false)
//@ConditionalOnProperty(prefix = "test", name="name1", havingValue = "zz",matchIfMissing = false)
public class BeanConfig {
@Bean
public TestBean testBean(){
return new TestBean("我是美猴王");
}
}
@SpringBootApplication
public class TestAppCommand implements CommandLineRunner {
@Autowired
private TestBean testBean;
public static void main(String[] args) {
SpringApplication.run(TestAppCommand.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println(testBean.getName());
}
}
@ConditionalOnJava
可以通过java的版本进行判断。
@Data
public class TestBean {
}
@Configuration
@ConditionalOnJava(JavaVersion.EIGHT)
public class BeanConfig {
@Bean
public TestBean testBean(){
return new TestBean();
}
}
public class TestApp {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test(){
Map<String,TestBean> map = context.getBeansOfType(TestBean.class);
System.out.println(map);
}
}
@ConditionalOnResource
通过指定的资源文件是否存在进行条件判断,比如判断ehcache.properties来决定是否自动装配ehcache组件。
@Data
public class TestBean {
}
@Configuration
@ConditionalOnResource(resources = "classpath:application.yml")
public class BeanConfig {
@Bean
public TestBean testBean(){
return new TestBean();
}
}
public class TestApp {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test(){
Map<String,TestBean> map = context.getBeansOfType(TestBean.class);
System.out.println(map);
}
}
@ConditionalOnSingleCandidate
这个还没有想到应用场景,条件通过的条件是:1 对应的bean容器中只有一个 2.对应的bean有多个,但是已经制定了PRIMARY。例子中,BeanB装配的时候需要看BeanA的装配情况,所以BeanBConfig要排在BeanAConfig之后.可以修改BeanAConfig,将@Primary注解去掉,或者把三个@Bean注解去掉,BeanB就不会实例化了。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BeanA {
private String name;
}
@Configuration
public class BeanAConfig {
@Bean
@Primary
public BeanA bean1(){
return new BeanA("bean1");
}
@Bean(autowireCandidate = false)
public BeanA bean2(){
return new BeanA("bean2");
}
//@Bean(autowireCandidate = false)
public BeanA bean3(){
return new BeanA("bean3");
}
}
@Data
public class BeanB {
}
@Configuration
@AutoConfigureAfter(BeanAConfig.class)
@ConditionalOnSingleCandidate(BeanA.class)
public class BeanBConfig {
@Bean
public BeanB targetBean(){
return new BeanB();
}
}
public class TestApp {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanAConfig.class, BeanBConfig.class);
@Test
public void test(){
Map<String,BeanA> map = context.getBeansOfType(BeanA.class);
System.out.println(map);
Map<String,BeanB> map2 = context.getBeansOfType(BeanB.class);
System.out.println(map2);
}
}
@ConditionalOnNotWebApplication & @ConditionalOnWebApplication
判断当前环境是否是Web应用。
来源:https://segmentfault.com/a/1190000018831198


猜你喜欢
- 外观模式: 又称门面模式: 外观Facade为子系统的一组接口提供一个一致界面,使得这组子系统易于使用(通过引入一个新的外观角色降低原系统复
- 本文实例为大家分享了Java swing读取txt文件实现学生考试系统的具体代码,供大家参考,具体内容如下主要实现了一个简单的倒计时答题系统
- 当我们需要在Unity客户端做一个限制功能,比如按钮 (最好是发送验证码按钮)要求每天只能点击三次,等到第二天又有三次机会,这个过程不涉及到
- 前言EasyCache升级兼容 Springboot2,有个业务系统启动总是会卡住,最后抛出超时异常,如下:java.util.concur
- 本文实例讲述了C#通过流写入一行数据到文件的方法。分享给大家供大家参考。具体如下:using System;using System.IO;
- 这篇文章主要介绍了Java通过Scanner了解if...else if语句,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 本文实例为大家分享了javaOpenCV-4.0.0 实时人脸识别,供大家参考,具体内容如下package com.xu.opencv;im
- 最近正在学习使用Android Studio,发现默认的Hello World程序界面和我们
- Prim算法介绍1.点睛在生成树的过程中,把已经在生成树中的节点看作一个集合,把剩下的节点看作另外一个集合,从连接两个集合的边中选择一条权值
- POM<dependency> <groupId>org.springframework.boot<
- 本文实例为大家分享了ImageSwitcher图像切换器的实现代码,供大家参考,具体内容如下描述在该实例中,提供一个图片切换器和两个点击按钮
- C#实现的Check Password,并根据输错密码的次数分情况锁定账户:如果输入错误3次,登录账户锁定5分钟并提示X点X分后重试登录。如
- Object 类位于 java.lang 包中,是所有 Java 类的祖先,Java 中的每个类都由它扩展而来。定义Java类时如果没有显示
- 这一篇文章涵盖了将 Shiro 集成到基于 Spring 的应用程序的方法。Shiro 的 Java Bean兼容性使它非常适合通过 Spr
- 本文实例为大家分享了JAVA实现人脸识别的具体代码,供大家参考,具体内容如下官方下载 安装文件 ,以win7为例,下载opencv-2.4.
- 第一步:图形验证码接口1.使用第三方的验证码生成工具Kaptchahttps://github.com/penggle/kaptcha@Co
- 需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合;要按照总分从高到低进行排序分析:1.创建学生类 成员变
- 在用C#开发Web应用时有个痛点,就是本机用VS开启Web应用调试时外部机器无法访问此Web应用。这里将会介绍如何通过设置允许局域网和外网机
- Android中实现拍照有两种方法,一种是调用系统自带的相机,然后使用其返回的照片数据。 还有一种是自己用Camera类和其他相关类实现相机
- 一、前言在C#中,由于有了垃圾回收机制的支持,对象的析构和以前的C++有了很大的不同,这就要求程序员在设计类型的时候,充分理解.NET的机制