Implementation of Condition Judgments in Spring Boot

  • 2021-07-18 08:04:33
  • OfStack

Those Conditional in Spring Boot

spring boot provides us with a rich Conditional to make it very convenient to add Bean to the container in the project. This paper mainly explains each annotation and explains its purpose with codes.

All ConditionalOnXXX annotations can be placed on class or method, and if the mode is on class, it will determine whether all @ Bean annotation methods in that class are executed.

@Conditional

The following other Conditional annotations are syntax sugar, and you can customize ConditionalOnXXX by the following methods

The Conditional annotation is defined as follows, receiving an class array that implements the Condition interface.


public @interface Conditional {
  Class<? extends Condition>[] value();
}

However, the Condition interface has only one matchs method, which returns the result of whether it matches or not.


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

Condition judgment is carried out through the operating system, so as to configure Bean. When Window, the Person object of Bill is instantiated, and when Linux, the Person object of Linus is instantiated.


//LinuxCondition, For convenience, remove the judgment code and return directly true It's over 
public class LinuxCondition implements Condition {
  @Override
  public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    return true;
  }
}

//WindowsCondition, For convenience, remove the judgment code and return directly false It's over 
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 class 
@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(" The current system is: " + osName);
    Map<String, Person> map = applicationContext.getBeansOfType(Person.class);
    System.out.println(map);
  }
}

Output result:

The current system is: Mac OS X
{linus=Person(name=Linus, age=48)}

@ConditionalOnBean & @ConditionalOnMissingBean

These two annotations will judge the Bean object in the Bean container. The example used is to instantiate a standby computer if there is no Computer instance during configuration.


@Data
@AllArgsConstructor
@ToString
public class Computer {
  private String name;
}

@Configuration
public class BeanConfig {
  @Bean(name = "notebookPC")
  public Computer computer1(){
    return new Computer(" Notebook computer ");
  }

  @ConditionalOnMissingBean(Computer.class)
  @Bean("reservePC")
  public Computer computer2(){
    return new Computer(" Backup 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);
  }
}

Modify BeanConfig. If you comment out the first @ Bean, the standby computer will be instantiated, otherwise the standby computer will not be instantiated

@ConditionalOnClass & @ConditionalOnMissingClass

This annotation determines whether there is a specified class on the classpath, 1 When I started to see it, I was confused. If there is no specified class on the classpath, the compilation will not pass... This is mainly used when integrating the third-party components with the same functions. As long as there is a class of this component on the classpath, it will be automatically configured. For example, when spring boot web automatically configures the view component, it uses Velocity, Thymeleaf or freemaker.

Examples are two sets of armor, A (light suit) and B (dark suit), and B is configured if A is not present.


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

Van is a samurai, fighting with suits


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Van {
  private Fighter fighter;
  public void fight(){
    fighter.fight();
  }
}

VanConfigA/B Instantiated Samurai


@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());
  }
}

Test class, by default, if the package AB is on the ClassPath, both sets will be loaded, A will be set to PRIMARY, and if FightA. class is removed from target class, only the package B will be loaded.


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

Also, try merging the two VanConfigA/B, put the annotation ConditionalOnClass on the method, and if you delete one package, it will run in error.

@ConditionalOnExpress

Judging by the condition of the expression, this function is common to @ ConditionalOnProperty in most cases, and the expression is 1 point more flexible because SpEL can be used. In the example, the value of test. enabled in properties will be judged. BeanConfig judges Boolean, string and number types respectively. Numbers have tried many other ways, such as using = = directly, and it seems that configured attributes will be treated as strings.


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

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

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

@ConditionalOnProperty

It is suitable for conditional judgment of a single Property, while the above @ ConditionalOnExpress is suitable for more complex situations, such as association comparison of multiple property. This example also gives three basic types of conditional judgment, but it seems that all of them can be regarded as strings...


@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(" I am the Monkey King ");
  }
}


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

@ConditionalOnJava

You can judge by the version of 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

Conditional judgment is made by whether the specified resource file exists, such as judging ehcache. properties to decide whether to automatically assemble ehcache components.


@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

This application scenario has not been thought of yet, and the condition passed is that there is only one bean container corresponding to 1. There are multiple bean, but PRIMARY has been formulated. In the example, the assembly of BeanB needs to look at the assembly of BeanA, so BeanBConfig should be ranked after BeanAConfig. You can modify BeanAConfig to remove the @ Primary annotation, or remove three @ Bean annotations, and BeanB will not be instantiated.


@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

Determine whether the current environment is an Web application.


Related articles: