Article directory
need
In the process of product docking, a public service connects multiple products, where product A is used RocketMQ
for 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 RocketMQ
the monitoring consumption @RocketMQMessageListener
annotation. 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
- In
SpringBoot
, you can use this annotation to control@Configuration
whether 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. - This annotation is
@Conditional
an 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
- The above explanation
@ConditionalOnProperty
can only be controlled preciselyhavingValue
by the value in the matching, and cannot be matched according to more attribute values (even if it has an arrayvalue
value, it can onlyhavingValue
be checked with it与
) - We can
@ConditionalOnExpression
check the expressions of other attribute values through annotations. @ConditionalOnExpression
It is an executionSpel
expression, 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.switch
is not false
a 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.switch
as false
a value, this class will not take effect.
Other use case examples
- Boolean attribute
@ConditionalOnExpression("${
conditional.configuration.switch:true}
- Multiple boolean properties, as long as one of them is satisfied
@ConditionalOnExpression("${conditional.configuration.switch1:true} || ${conditional.configuration.switch2:true}")
- config value equal to number
@ConditionalOnExpression("${conditional.configuration.switch} == 1")
@ConditionalOnExpression("${conditional.configuration.switch} != 1")
- The configuration value is the same as the specified string
@ConditionalOnExpression("'${conditional.configuration.switch}'.equals('yes')")
- Multiple configuration values are the same
@ConditionalOnExpression("'${conditional.configuration.str1}'.equals('${conditional.configuration.str2}')")
- … …
Summarize
I just wanted to use it at first @ConditionalOnProperty
, and it will be @ConditionalOnExpression
more refreshing after using it!