Spring Boot中的那些条件判断的实现方法

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应用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例

    前言 距离第一篇 Spring Boot 系列的博文 3 个月了.虽然 XML 形式是我比较推荐的,但是注解形式也是方便的.尤其一些小系统,快速的 CRUD 轻量级的系统. 这里感谢晓春 http://xchunzhao.tk/ 的 Pull Request,提供了 springboot-mybatis-annotation 的实现. 一.运行 springboot-mybatis-annotation 工程 然后Application 应用启动类的 main 函数,然后在浏览器访问: http

  • Spring boot中PropertySource注解的使用方法详解

    前言 本文将重点讲解一下Spring中@PropertySource注解的使用,如何通过PropertySource注解加载指定的配置文件.以及PropertySource注解与@ConfigurationProperties两个注解的配合使用.下面话不多说了,来随着小编来一起学习学习吧. 1.1. PropertySource注解加载指定的属性文件 Spring框架提供了PropertySource注解,目的是加载指定的属性文件,接下来我们看一下如何使用该注解.首先我们定义一个配置类,并在类中

  • SpringBoot中自定义注解实现控制器访问次数限制实例

    今天给大家介绍一下SpringBoot中如何自定义注解实现控制器访问次数限制. 在Web中最经常发生的就是利用恶性URL访问刷爆服务器之类的攻击,今天我就给大家介绍一下如何利用自定义注解实现这类攻击的防御操作. 其实这类问题一般的解决思路就是:在控制器中加入自定义注解实现访问次数限制的功能. 具体的实现过程看下面的例子: 步骤一:先定义一个注解类,下面看代码事例: package example.controller.limit; import org.springframework.core.

  • spring-boot通过@Scheduled配置定时任务及定时任务@Scheduled注解的方法

    串行的定时任务 @Component public class ScheduledTimer { private Logger logger = Logger.getLogger(this.getClass()); /** * 定时任务,1分钟执行1次,更新潜在客户超时客户共享状态 */ @Scheduled(cron="0 0/1 8-20 * * ?") public void executeUpdateCuTask() { Thread current = Thread.curr

  • 详解SpringBoot AOP 拦截器(Aspect注解方式)

    常用用于实现拦截的有:Filter.HandlerInterceptor.MethodInterceptor 第一种Filter属于Servlet提供的,后两者是spring提供的,HandlerInterceptor属于Spring MVC项目提供的,用来拦截请求,在MethodInterceptor之前执行. 实现一个HandlerInterceptor可以实现接口HandlerInterceptor,也可以继承HandlerInterceptorAdapter类,两种方法一样.这个不在本文

  • SpringBoot 注解事务声明式事务的方式

    springboot 对新人来说可能上手比springmvc要快,但是对于各位从springmvc转战到springboot的话,有些地方还需要适应下,尤其是xml配置.我个人是比较喜欢注解➕xml是因为看着方便,查找方便,清晰明了.但是xml完全可以使用注解代替,今天就扒一扒springboot中事务使用注解的玩法. springboot的事务也主要分为两大类,一是xml声明式事务,二是注解事务,注解事务也可以实现类似声明式事务的方法,关于注解声明式事务,目前网上搜索不到合适的资料,所以在这里

  • Spring Boot使用Value注解给静态变量赋值的方法

    昨天在使用@Value注解给静态变量赋值的时候,发现静态变量的值始终是null.后来搜索一下得知其中原因,Spring Boot 不允许/不支持把值注入到静态变量中.但是我们可以变通一下解决这个问题.因为Spring Boot 支持set方法注入,我们可以利用非静态set方法注入静态变量.废话不多说,贴上我昨天写的代码: @Component public class CoverImageUtil { private static String endpoint; private static

  • 详解Spring Boot集成MyBatis(注解方式)

    MyBatis是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集.spring Boot是能支持快速创建Spring应用的Java框架.本文通过一个例子来学习Spring Boot如何集成MyBatis,而且过程中不需要XML配置. 创建数据库 本文的例子使用MySQL数据库,首先创建一个用户表,执行sql语句如下: CREATE TABLE IF NOT EXISTS user ( `id` INT(10) NOT NULL A

  • SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解决方案

    hystrix参数使用方法 通过注解@HystrixCommand的commandProperties去配置, 如下就是hystrix命令超时时间命令执行超时时间,为1000ms和执行是不启用超时 @RestController public class MovieController { @Autowired private RestTemplate restTemplate; @GetMapping("/movie/{id}") @HystrixCommand(commandPro

  • Spring Boot中的那些条件判断的实现方法

    Spring Boot中的那些Conditional spring boot中为我们提供了丰富的Conditional来让我们得以非常方便的在项目中向容器中添加Bean.本文主要是对各个注解进行解释并辅以代码说明其用途. 所有ConditionalOnXXX的注解都可以放置在class或是method上,如果方式在class上,则会决定该class中所有的@Bean注解方法是否执行. @Conditional 下面其他的Conditional注解均是语法糖,可以通过下面的方法自定义Conditi

  • Spring Boot中自动化配置的利弊以及解决方法

    本文主要给大家介绍了关于Spring Boot自动化配置的利弊及解决方法的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: Spring Boot中的双刃剑:自动化配置 在之前的文章中,我们通过各种功能性示例体验了Spring Boot的自动化配置给我们所带来的超便利的新开发方式.但是,在一些情况下Spring Boot的自动化配置也会给我们惹来不少的麻烦,比如这些场景: 项目依赖复杂的情况下,由于依赖方的依赖组织不够严格,可能引入了一些实际我们不需要的依赖,从而导致我们

  • Spring boot中自定义Json参数解析器的方法

    一.介绍 用过springMVC/spring boot的都清楚,在controller层接受参数,常用的都是两种接受方式,如下 /** * 请求路径 http://127.0.0.1:8080/test 提交类型为application/json * 测试参数{"sid":1,"stuName":"里斯"} * @param str */ @RequestMapping(value = "/test",method = Re

  • Spring boot中@Conditional和spring boot的自动配置实例详解

    我们知道,spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子: Spring的JdbcTemplate是不是在Classpath里面?如果是,并且DataSource也存在,就自动配置一个JdbcTemplate的Bean Thymeleaf是不是在Classpath里面?如果是,则自动配置Thymeleaf的模板解析器.视图解析器.模板引擎 那个这个是怎么实现的呢?原因就在于它利用了Spring的条件化配置,条件化配置允许配置存在于应用中

  • Spring Boot中@ConditionalOnProperty的使用方法

    前言 在Spring Boot的自动配置中经常看到@ConditionalOnProperty注解的使用,本篇文章带大家来了解一下该注解的功能.下面话不多说了,来一起看看详细的介绍吧. Spring Boot中的使用 在Spring Boot的源码中,比如涉及到Http编码的自动配置.数据源类型的自动配置等大量的使用到了@ConditionalOnProperty的注解. HttpEncodingAutoConfiguration类中部分源代码: @Configuration(proxyBean

  • Spring Boot中的微信支付全过程(小程序)

    目录 前言 一.申请流程和步骤 二.注册商家 2.1商户平台 2.2商户id 三.API私钥(支付密钥) 四.商户签约微信支付产品 五.配置回调地址 六.小程序获取APPID 七.微信支付与小程序绑定 八.实战部分 8.1SpringBoot框架搭建 8.2微信支付相关接口 8.2.1小程序用户登录接口 8.2.2统一下单接口 8.2.3创建订单接口 8.2.4取消订单接口 8.2.5订单详情接口 8.2.6支付回调接口 前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握

  • Spring Boot 深入分析AutoConfigurationImportFilter自动化条件配置源码

    目录 1. AutoConfigurationImportFilter的作用 2. AutoConfigurationImportFilter UML类图说明 3. FilteringSpringBootCondition抽象类 4. AutoConfigurationImportSelector类 5. 总结 1. AutoConfigurationImportFilter的作用 之前讲解了SpringBoot的Conditional的自动化条件配置,我们分析了内部是如何具体实现,在整个实现当

  • Spring Boot 中starter的原理详析

    目录 1.springboot 的starter 的启动原理是什么 原理 来个例子 小结 2.springboot 是如何找到配置类的 3.springboot starter 的bean 是怎么加载到容器的 4.总结 前言: 今天介绍springboot ,也是写下springboot的插件机制,starter的原理,其实这个网上已经很多了,也是看了不少别人的文章,今天主要还是带着问题去记录下. 1.springboot 的starter 的启动原理是什么 原理 这个问题是很简单的,只要了解s

  • Spring自动配置之condition条件判断上篇

    目录 前言 引入一个例子 condition的一个案例 总结 前言 Condition是在Spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建Bean对象. 引入一个例子 SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如何知道要创建RedisTemplate的?在SpringBoot中获取应用上下文对象context,通过其getBean方法获取Bean对象 package cs.yangtze.springboot_condition; imp

  • Spring Boot中使用Spring-data-jpa实现数据库增删查改

    在实际开发过程中,对数据库的操作无非就"增删改查".就最为普遍的单表操作而言,除了表和字段不同外,语句都是类似的,开发人员需要写大量类似而枯燥的语句来完成业务逻辑. 为了解决这些大量枯燥的数据操作语句,我们第一个想到的是使用ORM框架,比如:Hibernate.通过整合Hibernate之后,我们以操作Java实体的方式最终将数据改变映射到数据库表中. 为了解决抽象各个Java实体基本的"增删改查"操作,我们通常会以泛型的方式封装一个模板Dao来进行抽象简化,但是这

随机推荐