SpringBoot(5-@Conditional条件注解) | 总字数: 4.6k | 阅读时长: 21分钟 | 浏览量: |
属性注入
@ConfigurationProperties+@Component
如果一个类只配置了@ConfigurationProperties 注解,而没有使用@Component 注解将该类加入到 IOC 容器中,那么它就不能完成 xxx.properties 文件和 Java Bean 的数据绑定
1 2 xiaomao.name =xiaomaomao xiaomao.age =27
1 2 3 4 5 6 7 8 9 10 @Component @ConfigurationProperties(prefix = "xiaomao") @Data public class MyConfigurationProperties { private String name; private Integer age; private String gender; }
@EnableConfigurationProperties
@EnableConfigurationProperties(A.class)的作用就是如果 A 这个类上使用了@ConfigurationProperties 注解,那么 A 这个类就会与 xxx.properties 进行动态绑定,并且会将 A 这个类加入 IOC 容器中,并交由 IOC 容器进行管理
1 2 3 4 5 6 7 8 9 @ConfigurationProperties(prefix = "xiaomao") @Data public class MyConfigurationProperties { private String name; private Integer age; private String gender; }
1 2 3 4 5 @Service @EnableConfigurationProperties(MyConfigurationProperties.class) public class HelloServiceImpl implements HelloService {}
@Conditional 扩展注解
Condition 处理类
条件注解
实例
解释
OnBeanCondition
@ConditionalOnBean
@ConditionalOnBean(DataSource.class)
Spring 容器中不存在对应的实例生效
OnBeanCondition
@ConditionalOnMissingBean
@ConditionalOnMissingBean(name = “redisTemplate”)
Spring 容器中不存在对应的实例生效
OnBeanCondition
@ConditionalOnSingleCandidate
@ConditionalOnSingleCandidate(FilteringNotifier.class)
Spring 容器中是否存在且只存在一个对应的实例,或者虽然有多个但 是指定首选的 Bean 生效
OnClassCondition
@ConditionalOnClass
@ConditionalOnClass(RedisOperations.class)
类加载器中存在对应的类生效
OnClassCondition
@ConditionalOnMissingClass
@ConditionalOnMissingClass(RedisOperations.class)
类加载器中不存在对应的类生效
OnExpressionCondition
@ConditionalOnExpression
@ConditionalOnExpression(“‘’${server.host}'==‘localhost’”)
判断 SpEL 表达式成立生效
@ConditionalOnJava
OnJavaCondition
@ConditionalOnJava(JavaVersion.EIGHT)
指定 Java 版本符合要求生效
@ConditionalOnProperty
OnPropertyCondition
@ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true)
应用环境中的属性满足条件生效
@ConditionalOnResource
OnResourceCondition
@ConditionalOnResource(resources =“mybatis.xml”)
存在指定的资源文件生效
@ConditionalOnWebApplication
OnWebApplicationCondition
当前应用是 Web 应用生效
@ConditionalOnNotWebApplication
OnWebApplicationCondition
当前应用不是 Web 应用生效
上面的扩展注解我们可以简单的分为以下几类:
Bean 作为条件:@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnSingleCandidate。
类作为条件:@ConditionalOnClass、@ConditionalOnMissingClass。
SpEL 表达式作为条件:@ConditionalOnExpression。
JAVA 版本作为条件:@ConditionalOnJava
配置属性作为条件:@ConditionalOnProperty。
资源文件作为条件:@ConditionalOnResource。
是否 Web 应用作为判断条件:@ConditionalOnWebApplication、@ConditionalOnNotWebApplication。
@ConditionalOnBean
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 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnBean { Class<?>[] value() default {}; String[] type() default {}; Class<? extends Annotation >[] annotation() default {}; String[] name() default {}; SearchStrategy search () default SearchStrategy.ALL; Class<?>[] parameterizedContainer() default {}; }
@ConditionalOnBean 对应的 Condition 处理类是 OnBeanCondition,如果 Spring 容器里面存在指定的 Bean 则生效。
1 2 3 4 5 6 @Bean @ConditionalOnBean(DataSource.class) public String onBeanCondition () { return "DataSource 存在时该 Bean 被创建" ; }
@ConditionalOnMissingBean
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 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnMissingBean { Class<?>[] value() default {}; String[] type() default {}; Class<?>[] ignored() default {}; String[] ignoredType() default {}; Class<? extends Annotation >[] annotation() default {}; String[] name() default {}; SearchStrategy search () default SearchStrategy.ALL; Class<?>[] parameterizedContainer() default {}; }
@ConditionalOnMissingBean 对应的 Condition 实现类是 OnBeanCondition,如果 Spring 容器里面不存在指定的 Bean 则生效。
1 2 3 4 5 6 7 8 9 @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate (RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate <>(); template.setConnectionFactory(redisConnectionFactory); return template; }
@ConditionalOnSingleCandidate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnSingleCandidate { Class<?> value() default Object.class; String type () default "" ; SearchStrategy search () default SearchStrategy.ALL; }
@ConditionalOnSingleCandidate 对应的 Condition 处理类是 OnBeanCondition,如果当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean 的时候则生效。
1 2 3 4 5 6 @Bean @ConditionalOnSingleCandidate(DataSource.class) public String onSingleCandidate () { return "存在唯一的 DataSource 实例时,该 Bean 被创建" ; }
@ConditionalOnClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass { Class<?>[] value() default {}; String[] name() default {}; }
@ConditionalOnClass 对应的 Condition 处理类是 OnClassCondition,如果当前类路径下面有指定的类的时候则生效。
1 2 3 4 5 6 @Bean @ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver") public String onClassCondition () { return "MySQL Driver 存在于 classpath 时,该 Bean 被创建" ; }
@ConditionalOnMissingClass
1 2 3 4 5 6 7 8 9 10 11 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnMissingClass { String[] value() default {}; }
@ConditionalOnMissingClass 对应的 Condition 处理类是 OnClassCondition,如果当前类路径下面没有指定的类的时候则生效。
1 2 3 4 5 6 @Bean @ConditionalOnMissingClass("com.mysql.cj.jdbc.Driver") public String onMissingClassCondition () { return "MySQL Driver 不存在于 classpath 时,该 Bean 被创建" ; }
@ConditionalOnExpression
1 2 3 4 5 6 7 8 9 10 11 @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnExpressionCondition.class) public @interface ConditionalOnExpression { String value () default "true" ; }
@ConditionalOnExpression 对应的 Condition 处理类是 OnExpressionCondition,只有当 SpEL 表达式满足条件的时候则生效。
1 2 3 4 5 6 @Bean @ConditionalOnExpression("'${custom.feature.enabled:false}' == 'true'") public String onExpressionCondition () { return "SpEL 表达式结果为 true 时,该 Bean 被创建" ; }
@ConditionalOnJava
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 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnJavaCondition.class) public @interface ConditionalOnJava { Range range () default Range.EQUAL_OR_NEWER; JavaVersion value () ; enum Range { EQUAL_OR_NEWER, OLDER_THAN } }
@ConditionalOnJava 对应的 Condition 处理类是 OnJavaCondition,只有当指定的 JAVA 版本条件满足的时候,才会创建对应的 Bean。
1 2 3 4 5 6 @Bean @ConditionalOnJava(range = ConditionalOnJava.Range.EQUAL_OR_NEWER, value = ConditionalOnJava.JavaVersion.ELEVEN) public String onJavaVersionCondition () { return "Java 版本 >= 11 时,该 Bean 被创建" ; }
@ConditionalProperty
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 @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { String[] value() default {}; String prefix () default "" ; String[] name() default {}; String havingValue () default "" ; boolean matchIfMissing () default false ; }
指定 prefix 和 name
1 2 3 config: person: enable: true
1 2 3 4 5 6 7 8 9 @Configuration public class MyBeanConfig { @Bean @ConditionalOnProperty(prefix = "config.person", name = "enable") public Person person1 () { return new Person ("Bill Gates" , 66 ); } }
只指定 name 或 value
1 2 3 4 5 6 7 8 9 @Configuration public class MyBeanConfig { @Bean @ConditionalOnProperty(value = "config.person.enable") public Person person1 () { return new Person ("Bill Gates" , 66 ); } }
指定 havingValue
指定了 havingValue,要把配置项的值与 havingValue 对比,一致则加载 Bean
注意:havingValue 可以不设置,只要配置项的值不是 false 或 “false”,都加载 Bean
1 2 3 4 5 6 7 8 9 @Configuration public class MyBeanConfig { @Bean @ConditionalOnProperty(prefix = "config.person", name = "enable", havingValue = "true") public Person person1 () { return new Person ("Bill Gates" , 66 ); } }
指定 matchIfMissing
配置文件缺少配置,但配置了 matchIfMissing = true,加载 Bean,否则不加载
1 2 3 4 5 6 7 8 9 @Configuration public class MyBeanConfig { @Bean @ConditionalOnProperty(prefix = "config.person", name = "enable", havingValue = "true", matchIfMissing = true) public Person person1 () { return new Person ("Bill Gates" , 66 ); } }
@ConditionalOnResource
1 2 3 4 5 6 7 8 9 10 11 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnResourceCondition.class) public @interface ConditionalOnResource { String[] resources() default {}; }
@ConditionalOnResource 对应的 Condition 处理类 OnResourceCondition,只有当指定的资源文件出现在 classpath 中则生效。
1 2 3 4 5 6 @Bean @ConditionalOnResource(resources = "classpath:config/application.yaml") public String onResourceCondition () { return "classpath 下存在 application.yaml 文件时,该 Bean 被创建" ; }
@ConditionalOnWebApplication
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 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnWebApplicationCondition.class) public @interface ConditionalOnWebApplication { Type type () default Type.ANY; enum Type { ANY, SERVLET, REACTIVE } }
@ConditionalOnWebApplication 对应的 Condition 处理类是 OnWebApplicationCondition,只有当当前项目是 Web 项目的时候则生效。
1 2 3 4 5 6 @Bean @ConditionalOnWebApplication public String onWebApplicationCondition () { return "Web 应用环境下,该 Bean 被创建" ; }
@ConditionalOnNotWebApplication
1 2 3 4 5 6 7 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnWebApplicationCondition.class) public @interface ConditionalOnNotWebApplication {}
@ConditionalOnNotWebApplication 对应的 Condition 处理类是 OnWebApplicationCondition,只有当当前项目不是 Web 项目的时候则生效。
1 2 3 4 5 6 @Bean @ConditionalOnNotWebApplication public String onNotWebApplicationCondition () { return "非 Web 应用环境下,该 Bean 被创建" ; }
@Conditional 自定义
仿照 OnPropertyCondition 源码进行修改,扩展注解 ConditionalOnPropertyExist,指定我们的 Condition 实现类 OnPropertyExistCondition,并且指定两个参数,一个是参数 name 用于指定属性,另一个参数 exist 用于指定是判断存在还是不存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Conditional(OnPropertyExistCondition.class) public @interface ConditionalOnPropertyExist { String name () default "" ; boolean exist () default true ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class OnPropertyExistCondition implements Condition { @Override public boolean matches (ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(ConditionalOnPropertyExist.class.getName()); if (annotationAttributes == null ) { return false ; } String propertyName = (String) annotationAttributes.get("name" ); boolean values = (boolean ) annotationAttributes.get("exist" ); String propertyValue = conditionContext.getEnvironment().getProperty(propertyName); if (values) { return !StringUtils.isEmpty(propertyValue); } else { return StringUtils.isEmpty(propertyValue); } } }
源码详解
条件判断的触发时机
Spring 在以下阶段触发条件判断:
阶段
触发场景
相关源码位置
配置类解析阶段
解析 @Configuration
类时
ConfigurationClassParser
Bean 方法注册阶段
处理 @Bean
方法时
ConfigurationClassBeanDefinitionReader
组件扫描阶段
扫描 @Component
及其派生注解时
ClassPathBeanDefinitionScanner
ConditionEvaluator
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 class ConditionEvaluator { private final ConditionContextImpl context; public ConditionEvaluator (@Nullable BeanDefinitionRegistry registry, @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { this .context = new ConditionContextImpl (registry, environment, resourceLoader); } public boolean shouldSkip (AnnotatedTypeMetadata metadata) { return shouldSkip(metadata, null ); } public boolean shouldSkip (@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false ; } if (phase == null ) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList <>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this .context.getClassLoader()); conditions.add(condition); } } AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null ; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this .context, metadata)) { return true ; } } return false ; } }
SpringBootCondition
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 public abstract class SpringBootCondition implements Condition { private final Log logger = LogFactory.getLog(getClass()); @Override public final boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { String classOrMethodName = getClassOrMethodName(metadata); try { ConditionOutcome outcome = getMatchOutcome(context, metadata); logOutcome(classOrMethodName, outcome); recordEvaluation(context, classOrMethodName, outcome); return outcome.isMatch(); } catch (NoClassDefFoundError ex) { throw new IllegalStateException ("Could not evaluate condition on " + classOrMethodName + " due to " + ex.getMessage() + " not found. Make sure your own configuration does not rely on " + "that class. This can also happen if you are " + "@ComponentScanning a springframework package (e.g. if you " + "put a @ComponentScan in the default package by mistake)" , ex); } catch (RuntimeException ex) { throw new IllegalStateException ("Error processing condition on " + getName(metadata), ex); } } private String getName (AnnotatedTypeMetadata metadata) { if (metadata instanceof AnnotationMetadata) { return ((AnnotationMetadata) metadata).getClassName(); } if (metadata instanceof MethodMetadata) { MethodMetadata methodMetadata = (MethodMetadata) metadata; return methodMetadata.getDeclaringClassName() + "." + methodMetadata.getMethodName(); } return metadata.toString(); } private static String getClassOrMethodName (AnnotatedTypeMetadata metadata) { if (metadata instanceof ClassMetadata) { ClassMetadata classMetadata = (ClassMetadata) metadata; return classMetadata.getClassName(); } MethodMetadata methodMetadata = (MethodMetadata) metadata; return methodMetadata.getDeclaringClassName() + "#" + methodMetadata.getMethodName(); } protected final void logOutcome (String classOrMethodName, ConditionOutcome outcome) { if (this .logger.isTraceEnabled()) { this .logger.trace(getLogMessage(classOrMethodName, outcome)); } } private StringBuilder getLogMessage (String classOrMethodName, ConditionOutcome outcome) { StringBuilder message = new StringBuilder (); message.append("Condition " ); message.append(ClassUtils.getShortName(getClass())); message.append(" on " ); message.append(classOrMethodName); message.append(outcome.isMatch() ? " matched" : " did not match" ); if (StringUtils.hasLength(outcome.getMessage())) { message.append(" due to " ); message.append(outcome.getMessage()); } return message; } private void recordEvaluation (ConditionContext context, String classOrMethodName, ConditionOutcome outcome) { if (context.getBeanFactory() != null ) { ConditionEvaluationReport.get(context.getBeanFactory()).recordConditionEvaluation(classOrMethodName, this , outcome); } } public abstract ConditionOutcome getMatchOutcome (ConditionContext context, AnnotatedTypeMetadata metadata) ; protected final boolean anyMatches (ConditionContext context, AnnotatedTypeMetadata metadata, Condition... conditions) { for (Condition condition : conditions) { if (matches(context, metadata, condition)) { return true ; } } return false ; } protected final boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata, Condition condition) { if (condition instanceof SpringBootCondition) { return ((SpringBootCondition) condition).getMatchOutcome(context, metadata).isMatch(); } return condition.matches(context, metadata); } }
OnPropertyCondition
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 @Order(Ordered.HIGHEST_PRECEDENCE + 40) class OnPropertyCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome (ConditionContext context, AnnotatedTypeMetadata metadata) { List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap( metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName())); List<ConditionMessage> noMatch = new ArrayList <>(); List<ConditionMessage> match = new ArrayList <>(); for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) { ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment()); (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage()); } if (!noMatch.isEmpty()) { return ConditionOutcome.noMatch(ConditionMessage.of(noMatch)); } return ConditionOutcome.match(ConditionMessage.of(match)); } private List<AnnotationAttributes> annotationAttributesFromMultiValueMap ( MultiValueMap<String, Object> multiValueMap) { List<Map<String, Object>> maps = new ArrayList <>(); multiValueMap.forEach((key, value) -> { for (int i = 0 ; i < value.size(); i++) { Map<String, Object> map; if (i < maps.size()) { map = maps.get(i); } else { map = new HashMap <>(); maps.add(map); } map.put(key, value.get(i)); } }); List<AnnotationAttributes> annotationAttributes = new ArrayList <>(maps.size()); for (Map<String, Object> map : maps) { annotationAttributes.add(AnnotationAttributes.fromMap(map)); } return annotationAttributes; } private ConditionOutcome determineOutcome (AnnotationAttributes annotationAttributes, PropertyResolver resolver) { Spec spec = new Spec (annotationAttributes); List<String> missingProperties = new ArrayList <>(); List<String> nonMatchingProperties = new ArrayList <>(); spec.collectProperties(resolver, missingProperties, nonMatchingProperties); if (!missingProperties.isEmpty()) { return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec) .didNotFind("property" , "properties" ).items(Style.QUOTE, missingProperties)); } if (!nonMatchingProperties.isEmpty()) { return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec) .found("different value in property" , "different value in properties" ) .items(Style.QUOTE, nonMatchingProperties)); } return ConditionOutcome .match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched" )); } private static class Spec { private final String prefix; private final String havingValue; private final String[] names; private final boolean matchIfMissing; Spec(AnnotationAttributes annotationAttributes) { String prefix = annotationAttributes.getString("prefix" ).trim(); if (StringUtils.hasText(prefix) && !prefix.endsWith("." )) { prefix = prefix + "." ; } this .prefix = prefix; this .havingValue = annotationAttributes.getString("havingValue" ); this .names = getNames(annotationAttributes); this .matchIfMissing = annotationAttributes.getBoolean("matchIfMissing" ); } private String[] getNames(Map<String, Object> annotationAttributes) { String[] value = (String[]) annotationAttributes.get("value" ); String[] name = (String[]) annotationAttributes.get("name" ); Assert.state(value.length > 0 || name.length > 0 , "The name or value attribute of @ConditionalOnProperty must be specified" ); Assert.state(value.length == 0 || name.length == 0 , "The name and value attributes of @ConditionalOnProperty are exclusive" ); return (value.length > 0 ) ? value : name; } private void collectProperties (PropertyResolver resolver, List<String> missing, List<String> nonMatching) { for (String name : this .names) { String key = this .prefix + name; if (resolver.containsProperty(key)) { if (!isMatch(resolver.getProperty(key), this .havingValue)) { nonMatching.add(name); } } else { if (!this .matchIfMissing) { missing.add(name); } } } } private boolean isMatch (String value, String requiredValue) { if (StringUtils.hasLength(requiredValue)) { return requiredValue.equalsIgnoreCase(value); } return !"false" .equalsIgnoreCase(value); } @Override public String toString () { StringBuilder result = new StringBuilder (); result.append("(" ); result.append(this .prefix); if (this .names.length == 1 ) { result.append(this .names[0 ]); } else { result.append("[" ); result.append(StringUtils.arrayToCommaDelimitedString(this .names)); result.append("]" ); } if (StringUtils.hasLength(this .havingValue)) { result.append("=" ).append(this .havingValue); } result.append(")" ); return result.toString(); } } }