diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java index fc0617492544..aa708df0dd07 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 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,20 +17,21 @@ 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.stream.Collectors; import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotationPredicates; import org.springframework.core.annotation.Order; 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; /** @@ -47,8 +48,10 @@ class OnPropertyCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - List allAnnotationAttributes = annotationAttributesFromMultiValueMap( - metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName())); + List allAnnotationAttributes = metadata.getAnnotations() + .stream(ConditionalOnProperty.class.getName()) + .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)) + .map(MergedAnnotation::asAnnotationAttributes).collect(Collectors.toList()); List noMatch = new ArrayList<>(); List match = new ArrayList<>(); for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) { @@ -61,29 +64,6 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM return ConditionOutcome.match(ConditionMessage.of(match)); } - private List annotationAttributesFromMultiValueMap( - MultiValueMap multiValueMap) { - List> maps = new ArrayList<>(); - multiValueMap.forEach((key, value) -> { - for (int i = 0; i < value.size(); i++) { - Map map; - if (i < maps.size()) { - map = maps.get(i); - } - else { - map = new HashMap<>(); - maps.add(map); - } - map.put(key, value.get(i)); - } - }); - List annotationAttributes = new ArrayList<>(maps.size()); - for (Map map : maps) { - annotationAttributes.add(AnnotationAttributes.fromMap(map)); - } - return annotationAttributes; - } - private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) { Spec spec = new Spec(annotationAttributes); List missingProperties = new ArrayList<>(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnPropertyTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnPropertyTests.java index ade027064d70..4bd89c1b5a56 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnPropertyTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnPropertyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -31,6 +31,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.StandardEnvironment; @@ -246,6 +247,31 @@ void metaAndDirectAnnotationConditionMatchesWhenBothPropertiesAreSet() { assertThat(this.context.containsBean("foo")).isTrue(); } + @Test + void metaAnnotationWithAliasConditionMatchesWhenPropertyIsSet() { + load(MetaAnnotationWithAlias.class, "my.feature.enabled=true"); + assertThat(this.context.containsBean("foo")).isTrue(); + } + + @Test + void metaAndDirectAnnotationWithAliasConditionDoesNotMatchWhenOnlyMetaPropertyIsSet() { + load(MetaAnnotationAndDirectAnnotationWithAlias.class, "my.feature.enabled=true"); + assertThat(this.context.containsBean("foo")).isFalse(); + } + + @Test + void metaAndDirectAnnotationWithAliasConditionDoesNotMatchWhenOnlyDirectPropertyIsSet() { + load(MetaAnnotationAndDirectAnnotationWithAlias.class, "my.other.feature.enabled=true"); + assertThat(this.context.containsBean("foo")).isFalse(); + } + + @Test + void metaAndDirectAnnotationWithAliasConditionMatchesWhenBothPropertiesAreSet() { + load(MetaAnnotationAndDirectAnnotationWithAlias.class, "my.feature.enabled=true", + "my.other.feature.enabled=true"); + assertThat(this.context.containsBean("foo")).isTrue(); + } + private void load(Class config, String... environment) { TestPropertyValues.of(environment).applyTo(this.environment); this.context = new SpringApplicationBuilder(config).environment(this.environment).web(WebApplicationType.NONE) @@ -416,4 +442,37 @@ String foo() { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnMyFeatureWithAlias("my.feature") + static class MetaAnnotationWithAlias { + + @Bean + String foo() { + return "foo"; + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnMyFeatureWithAlias("my.feature") + @ConditionalOnProperty(prefix = "my.other.feature", name = "enabled", havingValue = "true") + static class MetaAnnotationAndDirectAnnotationWithAlias { + + @Bean + String foo() { + return "foo"; + } + + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD }) + @ConditionalOnProperty(name = "enabled", havingValue = "true") + @interface ConditionalOnMyFeatureWithAlias { + + @AliasFor(annotation = ConditionalOnProperty.class, attribute = "prefix") + String value(); + + } + }