mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Allow @ConditionalOnProperty to be used as a meta-annotation
Closes gh-5819
This commit is contained in:
parent
147956a7b2
commit
34420a8768
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,8 +17,10 @@
|
||||
package org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
@ -27,6 +29,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@ -35,6 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Maciej Walkowiak
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.1.0
|
||||
* @see ConditionalOnProperty
|
||||
*/
|
||||
@ -43,10 +47,57 @@ class OnPropertyCondition extends SpringBootCondition {
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
|
||||
metadata.getAllAnnotationAttributes(
|
||||
ConditionalOnProperty.class.getName()));
|
||||
List<ConditionOutcome> noMatchOutcomes = findNoMatchOutcomes(
|
||||
allAnnotationAttributes, context.getEnvironment());
|
||||
if (noMatchOutcomes.isEmpty()) {
|
||||
return ConditionOutcome.match();
|
||||
}
|
||||
return ConditionOutcome.noMatch(getCompositeMessage(noMatchOutcomes));
|
||||
}
|
||||
|
||||
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(
|
||||
metadata.getAnnotationAttributes(ConditionalOnProperty.class.getName()));
|
||||
private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(
|
||||
MultiValueMap<String, Object> multiValueMap) {
|
||||
List<Map<String, Object>> maps = new ArrayList<Map<String, Object>>();
|
||||
for (Entry<String, List<Object>> entry : multiValueMap.entrySet()) {
|
||||
for (int i = 0; i < entry.getValue().size(); i++) {
|
||||
Map<String, Object> map;
|
||||
if (i < maps.size()) {
|
||||
map = maps.get(i);
|
||||
}
|
||||
else {
|
||||
map = new HashMap<String, Object>();
|
||||
maps.add(map);
|
||||
}
|
||||
map.put(entry.getKey(), entry.getValue().get(i));
|
||||
}
|
||||
}
|
||||
List<AnnotationAttributes> annotationAttributes = new ArrayList<AnnotationAttributes>(
|
||||
maps.size());
|
||||
for (Map<String, Object> map : maps) {
|
||||
annotationAttributes.add(AnnotationAttributes.fromMap(map));
|
||||
}
|
||||
return annotationAttributes;
|
||||
}
|
||||
|
||||
private List<ConditionOutcome> findNoMatchOutcomes(
|
||||
List<AnnotationAttributes> allAnnotationAttributes,
|
||||
PropertyResolver resolver) {
|
||||
List<ConditionOutcome> noMatchOutcomes = new ArrayList<ConditionOutcome>(
|
||||
allAnnotationAttributes.size());
|
||||
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
|
||||
ConditionOutcome outcome = determineOutcome(annotationAttributes, resolver);
|
||||
if (!outcome.isMatch()) {
|
||||
noMatchOutcomes.add(outcome);
|
||||
}
|
||||
}
|
||||
return noMatchOutcomes;
|
||||
}
|
||||
|
||||
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,
|
||||
PropertyResolver resolver) {
|
||||
String prefix = annotationAttributes.getString("prefix").trim();
|
||||
if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
|
||||
prefix = prefix + ".";
|
||||
@ -56,7 +107,6 @@ class OnPropertyCondition extends SpringBootCondition {
|
||||
boolean relaxedNames = annotationAttributes.getBoolean("relaxedNames");
|
||||
boolean matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
|
||||
|
||||
PropertyResolver resolver = context.getEnvironment();
|
||||
if (relaxedNames) {
|
||||
resolver = new RelaxedPropertyResolver(resolver, prefix);
|
||||
}
|
||||
@ -91,7 +141,6 @@ class OnPropertyCondition extends SpringBootCondition {
|
||||
message.append("expected '").append(expected).append("' for properties ")
|
||||
.append(expandNames(prefix, nonMatchingProperties));
|
||||
}
|
||||
|
||||
return ConditionOutcome.noMatch(message.toString());
|
||||
}
|
||||
|
||||
@ -122,4 +171,15 @@ class OnPropertyCondition extends SpringBootCondition {
|
||||
return expanded.toString();
|
||||
}
|
||||
|
||||
private String getCompositeMessage(List<ConditionOutcome> noMatchOutcomes) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (ConditionOutcome noMatchOutcome : noMatchOutcomes) {
|
||||
if (message.length() > 0) {
|
||||
message.append(". ");
|
||||
}
|
||||
message.append(noMatchOutcome.getMessage().trim());
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@ -37,6 +42,7 @@ import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
|
||||
* @author Maciej Walkowiak
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ConditionalOnPropertyTests {
|
||||
|
||||
@ -227,6 +233,44 @@ public class ConditionalOnPropertyTests {
|
||||
load(NameAndValueAttribute.class, "some.property");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAnnotationConditionMatchesWhenPropertyIsSet() throws Exception {
|
||||
load(MetaAnnotation.class, "my.feature.enabled=true");
|
||||
assertTrue(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAnnotationConditionDoesNotMatchWhenPropertyIsNotSet()
|
||||
throws Exception {
|
||||
load(MetaAnnotation.class);
|
||||
assertFalse(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAndDirectAnnotationConditionDoesNotMatchWhenOnlyDirectPropertyIsSet() {
|
||||
load(MetaAnnotationAndDirectAnnotation.class, "my.other.feature.enabled=true");
|
||||
assertFalse(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAndDirectAnnotationConditionDoesNotMatchWhenOnlyMetaPropertyIsSet() {
|
||||
load(MetaAnnotationAndDirectAnnotation.class, "my.feature.enabled=true");
|
||||
assertFalse(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAndDirectAnnotationConditionDoesNotMatchWhenNeitherPropertyIsSet() {
|
||||
load(MetaAnnotationAndDirectAnnotation.class);
|
||||
assertFalse(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAndDirectAnnotationConditionMatchesWhenBothPropertiesAreSet() {
|
||||
load(MetaAnnotationAndDirectAnnotation.class, "my.feature.enabled=true",
|
||||
"my.other.feature.enabled=true");
|
||||
assertTrue(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
private void load(Class<?> config, String... environment) {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(this.context, environment);
|
||||
@ -390,4 +434,32 @@ public class ConditionalOnPropertyTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMyFeature
|
||||
protected static class MetaAnnotation {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMyFeature
|
||||
@ConditionalOnProperty(prefix = "my.other.feature", name = "enabled", havingValue = "true", matchIfMissing = false)
|
||||
protected static class MetaAnnotationAndDirectAnnotation {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@ConditionalOnProperty(prefix = "my.feature", name = "enabled", havingValue = "true", matchIfMissing = false)
|
||||
public @interface ConditionalOnMyFeature {
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user