SpringBoot - Detailed explanation and use of @ConditionalOnProperty and @ConditionalOnProperty annotations

need

  In the process of product docking, a public service connects multiple products, where product A is used RocketMQfor consumption, and product B does not need to be consumed. At this point, the code is shared, how can product B not be consumed?
  After studying some methods, the first thing that comes to mind is how to invalidate RocketMQthe monitoring consumption @RocketMQMessageListenerannotation. After researching for a while, the annotation itself has no switch, and I have not found a suitable method to invalidate the specified annotation. In another direction: conditional annotations. The pro-test is effective!

@ConditionalOnProperty annotation

introduce

  1. In SpringBoot, you can use this annotation to control @Configurationwhether it takes effect. At the same time, we can use this annotation to judge whether a property attribute conforms to the configuration value we set. If it matches, the class or method modified by the annotation will take effect, otherwise it will not take effect.
  2. This annotation is @Conditionalan extension annotation for .

Source code analysis

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure.condition;

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;
import org.springframework.context.annotation.Conditional;

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({
    
    OnPropertyCondition.class})
public @interface ConditionalOnProperty {
    
    
	//数组,获取property对应的值,与name属性不可以同时使用
    String[] value() default {
    
    };
	//属性名称值的前缀
    String prefix() default "";
	//数组,配置属性值的名称,可与prefix组合使用,不可与value属性同时使用
    String[] name() default {
    
    };
	//比较和name属性值获取到的属性值是否相同,与name组合使用,若true,则加载配置
    String havingValue() default "";
	//缺少配置是否匹配加载,若为true,则表示无该name相关属性值,也加载。反之,不加载。
    boolean matchIfMissing() default false;
}

Example of use

New bean class

@Data
public class PersonBean {
    
    
    /**
     * name
     */
    private String name;
    /**
     * age
     */
    private int age;
}

Annotation classes to test

/**
 * @Description 测试bean配置
 * @Author LiaoJy
 * @Date 2023/4/17
 */
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "conditional", name = "configuration.switch", havingValue = "true", matchIfMissing = true)
public class BeanConfigTest {
    
    

    @Bean
    public PersonBean personBean() {
    
    
        PersonBean personBean = new PersonBean();
        personBean.setAge(30);
        personBean.setName("andya");
        log.info("========person bean init========");
        return personBean;
    }

}

Configuration Scenario 1

conditional:
  configuration:
    switch: false

console log results

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

... ...
2023-04-17 14:14:11.620  INFO 25494 --- [           main] c.test.selfcoding.SelfCodingApplication  : No active profile set, falling back to 1 default profile: "default"
2023-04-17 14:14:12.175  INFO 25494 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8001 (http)
2023-04-17 14:14:12.182  INFO 25494 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-04-17 14:14:12.182  INFO 25494 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.64]
2023-04-17 14:14:12.259  INFO 25494 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-04-17 14:14:12.259  INFO 25494 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 609 ms
2023-04-17 14:14:12.445  INFO 25494 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8001 (http) with context path ''
2023-04-17 14:14:12.451  INFO 25494 --- [           main] c.test.selfcoding.SelfCodingApplication  : Started SelfCodingApplication in 1.123 seconds (JVM running for 1.387)

As a result, the log is not printed ========person bean init========and the class does not take effect.

Configuration Scenario 2

Do not configure, or configure as follows

conditional:
  configuration:
    switch: true

console log results

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

... ...
2023-04-17 14:17:35.172  INFO 25577 --- [           main] c.test.selfcoding.SelfCodingApplication  : No active profile set, falling back to 1 default profile: "default"
2023-04-17 14:17:35.771  INFO 25577 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8001 (http)
2023-04-17 14:17:35.777  INFO 25577 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-04-17 14:17:35.777  INFO 25577 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.64]
2023-04-17 14:17:35.859  INFO 25577 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-04-17 14:17:35.859  INFO 25577 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 655 ms
2023-04-17 14:17:35.899  INFO 25577 --- [           main] c.test.selfcoding.config.BeanConfigTest  : ========person bean init========
2023-04-17 14:17:36.134  INFO 25577 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8001 (http) with context path ''
2023-04-17 14:17:36.141  INFO 25577 --- [           main] c.test.selfcoding.SelfCodingApplication  : Started SelfCodingApplication in 1.421 seconds (JVM running for 1.775)

You can see that the log is printed ========person bean init========and the class takes effect.

@ConditionalOnExpression

introduce

  1. The above explanation @ConditionalOnPropertycan only be controlled precisely havingValueby the value in the matching, and cannot be matched according to more attribute values ​​(even if it has an array valuevalue, it can only havingValuebe checked with it )
  2. We can @ConditionalOnExpressioncheck the expressions of other attribute values ​​​​through annotations.
  3. @ConditionalOnExpressionIt is an execution Spelexpression, and judges whether the condition is met according to the returned Boolean value.

source code

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure.condition;

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;
import org.springframework.context.annotation.Conditional;

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({
    
    OnExpressionCondition.class})
public @interface ConditionalOnExpression {
    
    
	//Spel表达式
    String value() default "true";
}

Example of use

Annotation class to be tested

/**
 * @Description 测试bean配置
 * @Author LiaoJy
 * @Date 2023/4/17
 */
@Slf4j
@Configuration
//@ConditionalOnProperty(prefix = "conditional", name = "configuration.switch", havingValue = "true", matchIfMissing = true)
@ConditionalOnExpression("!'${conditional.configuration.switch}'.equals('false')")
public class BeanConfigTest {
    
    

    @Bean
    public PersonBean personBean() {
    
    
        PersonBean personBean = new PersonBean();
        personBean.setAge(30);
        personBean.setName("andya");
        log.info("========person bean init========");
        return personBean;
    }

}

At this time, as long as the configuration conditional.configuration.switchis not falsea value, this class will take effect.

Annotation class 2 to be tested

/**
 * @Description 测试bean配置
 * @Author LiaoJy
 * @Date 2023/4/17
 */
@Slf4j
@Configuration
//@ConditionalOnProperty(prefix = "conditional", name = "configuration.switch", havingValue = "true", matchIfMissing = true)
@ConditionalOnExpression("${conditional.configuration.switch:true}")
public class BeanConfigTest {
    
    

    @Bean
    public PersonBean personBean() {
    
    
        PersonBean personBean = new PersonBean();
        personBean.setAge(30);
        personBean.setName("andya");
        log.info("========person bean init========");
        return personBean;
    }

}

At this time, only if it is configured conditional.configuration.switchas falsea value, this class will not take effect.

Other use case examples

  1. Boolean attribute
@ConditionalOnExpression("${
    
    conditional.configuration.switch:true}
  1. Multiple boolean properties, as long as one of them is satisfied
@ConditionalOnExpression("${conditional.configuration.switch1:true} || ${conditional.configuration.switch2:true}")
  1. config value equal to number
@ConditionalOnExpression("${conditional.configuration.switch} == 1")
@ConditionalOnExpression("${conditional.configuration.switch} != 1")
  1. The configuration value is the same as the specified string
@ConditionalOnExpression("'${conditional.configuration.switch}'.equals('yes')")
  1. Multiple configuration values ​​are the same
@ConditionalOnExpression("'${conditional.configuration.str1}'.equals('${conditional.configuration.str2}')")
  1. … …

Summarize

  I just wanted to use it at first @ConditionalOnProperty, and it will be @ConditionalOnExpressionmore refreshing after using it!

Guess you like

Origin blog.csdn.net/Andya_net/article/details/130196150