这里介绍SpringBoot的@ConditionalOn系列注解,以及如何自定义实现条件注解
@ConditionalOn系列注解
对于@ConditionalOn系列注解而言
- 当添加在Java配置类(即被@Configuration注解修饰的类)上后,则只有符合期望条件后,才会对该Java配置类进行实例化
- 当添加在Java配置类中被@Bean注解修饰的方法上后,则只有符合期望条件后,才会调用该方法来创建相应的bean对象
常用地条件注解有:
- @ConditionalOnClass注解:存在指定类时,才算符合条件
- @ConditionalOnMissingClass注解:不存在指定类时,才算符合条件
- @ConditionalOnBean注解:存在指定名称的bean时,才算符合条件
- @ConditionalOnMissingBean注解:不存在指定名称的bean时,才算符合条件
- @ConditionalOnProperty注解:其prefix属性用于指定检查的配置项的名称前缀;其name属性用于指定检查的配置项的名称;其havingValue属性用于指定配置项的期望值。具体地,如果配置项的值和该属性值相等,则条件成立;此外,当该属性配置为””空字符串,则意为相应配置项的值不为false时条件均成立;其matchIfMissing属性设置为true后,即使指定的配置项不存在,条件也会成立
测试:添加在类上
符合期望条件
这里我们将条件注解添加在Java配置类(即被@Configuration注解修饰的类)上,来测试符合期望条件的场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.context.annotation.Configuration;
@Configuration public class MyConfig1 { public MyConfig1() { System.out.println("<My Config 1>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(name = "com.aaron.application.SpringSpi.ConditionalOnTest.StuInfo") @Configuration public class MyConfig2 { public MyConfig2() { System.out.println("<My Config 2>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.context.annotation.Configuration;
@ConditionalOnMissingClass("com.aaron.application.SpringSpi.ConditionalOnTest.StuInfo123") @Configuration public class MyConfig3 { public MyConfig3() { System.out.println("<My Config 3>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(name="stuInfo") @Configuration public class MyConfig4 { public MyConfig4() { System.out.println("<My Config 4>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Configuration;
@ConditionalOnMissingBean(name="stuInfo123") @Configuration public class MyConfig5 { public MyConfig5() { System.out.println("<My Config 5>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration;
@ConditionalOnProperty(prefix = "aaron.config", name="age", havingValue = "", matchIfMissing = false) @Configuration public class MyConfig6 { public MyConfig6() { System.out.println("<My Config 6>: 被实例化"); } }
|
同时为了测试各种条件,这里添加一个StuInfo类,同时添加了@Component注解。使其成为一个bean
1 2 3 4 5 6 7
| package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.stereotype.Component;
@Component public class StuInfo { }
|
此外为了测试@ConditionalOnProperty注解,在配置文件application.properties中添加下述配置
1 2 3
| ... aaron.config.age=14 ...
|
测试结果如下所示,符合预期
不符合期望条件
现在我们来修改下上述各条件注解上指定的条件,使其不符合期望条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.context.annotation.Configuration;
@Configuration public class MyConfig1 { public MyConfig1() { System.out.println("<My Config 1>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(name = "com.aaron.application.SpringSpi.ConditionalOnTest.StuInfo123") @Configuration public class MyConfig2 { public MyConfig2() { System.out.println("<My Config 2>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.context.annotation.Configuration;
@ConditionalOnMissingClass("com.aaron.application.SpringSpi.ConditionalOnTest.StuInfo") @Configuration public class MyConfig3 { public MyConfig3() { System.out.println("<My Config 3>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(name="stuInfo123") @Configuration public class MyConfig4 { public MyConfig4() { System.out.println("<My Config 4>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Configuration;
@ConditionalOnMissingBean(name="stuInfo") @Configuration public class MyConfig5 { public MyConfig5() { System.out.println("<My Config 5>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration;
@ConditionalOnProperty(prefix = "aaron.config", name="age", havingValue = "996", matchIfMissing = false) @Configuration public class MyConfig6 { public MyConfig6() { System.out.println("<My Config 6>: 被实例化"); } }
|
测试结果如下所示,符合预期
测试:添加在方法上
这里我们将条件注解添加在Java配置类中被@Bean注解修饰的方法上,来进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.aaron.application.SpringSpi.ConditionalOnTest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class MyConfig7 {
public MyConfig7() { System.out.println("<My Config 7>: 被实例化"); }
@ConditionalOnClass(name = "com.aaron.application.SpringSpi.ConditionalOnTest.StuInfo") @Bean public Object buildObjA() { System.out.println("bean A 被创建、实例化"); return new Integer(2); }
@ConditionalOnClass(name = "com.aaron.application.SpringSpi.ConditionalOnTest.StuInfo123") @Bean public Object buildObj() { System.out.println("bean B 被创建、实例化"); return new Integer(7); } }
|
测试结果如下所示,符合预期
自定义条件注解
事实上,我们还可以通过@Conditional注解来自定义条件注解。具体地,只需给@Conditional注解的value属性设置自定义规则的某个类即可。对于该自定义类而言,需实现Condition接口的matches方法来自定义规则。当该方法返回true为符合期望条件;返回false为不符合期望条件
这里我们想自定义一个条件注解:通过配置项的值是否位于指定区间,来判定是否符合期望条件。我们可以定义一个组合注解@ConditionalOnPropertyRange。其一方面拥有自定义的name、min、max属性;另一方面,在该注解上添加了@Conditional注解,同时指定相应的规则类为OnPropertyRangeCondition
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.aaron.application.SpringSpi.CustomConditionalTest;
import org.springframework.context.annotation.Conditional; import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnPropertyRangeCondition.class) public @interface ConditionalOnPropertyRange { String name() default "";
int min() default -1;
int max() default -1; }
|
这里相应的规则类OnPropertyRangeCondition实现如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.aaron.application.SpringSpi.CustomConditionalTest;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map;
public class OnPropertyRangeCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnPropertyRange.class.getName()); if (annotationAttributes == null || annotationAttributes.isEmpty()) { return false; }
String propertyName = (String)annotationAttributes.get("name"); int min = (int) annotationAttributes.get("min"); int max = (int) annotationAttributes.get("max"); Integer propertyValue = context.getEnvironment().getProperty(propertyName, Integer.class);
if( propertyValue==null || propertyValue<min || propertyValue>max ) { return false; }
return true; } }
|
现在我们来提供两个配置类,验证我们自定义的条件注解@ConditionalOnPropertyRange
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.aaron.application.SpringSpi.CustomConditionalTest;
import org.springframework.context.annotation.Configuration;
@ConditionalOnPropertyRange(name="aaron.config.weight", min=50, max=60) @Configuration public class MyConfig8 { public MyConfig8() { System.out.println("<My Config 8>: 被实例化"); } }
...
package com.aaron.application.SpringSpi.CustomConditionalTest;
import org.springframework.context.annotation.Configuration;
@ConditionalOnPropertyRange(name="aaron.config.weight", min=250, max=260) @Configuration public class MyConfig9 { public MyConfig9() { System.out.println("<My Config 9>: 被实例化"); } }
|
同时,在配置文件application.properties中添加下述配置
1 2 3
| ... aaron.config.weight=55 ...
|
测试结果如下所示,符合预期