Skip to content

Commit 1c61e59

Browse files
committed
Prohibit unnecessary values on @ConditionalOnMissingBean
Closes gh-42941
1 parent 1f7d8dd commit 1c61e59

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,19 @@
2424
import java.nio.file.StandardOpenOption;
2525
import java.util.Collections;
2626
import java.util.List;
27+
import java.util.Map;
2728
import java.util.Objects;
2829
import java.util.function.Supplier;
2930
import java.util.stream.Collectors;
3031

3132
import com.tngtech.archunit.base.DescribedPredicate;
33+
import com.tngtech.archunit.core.domain.JavaAnnotation;
3234
import com.tngtech.archunit.core.domain.JavaClass;
3335
import com.tngtech.archunit.core.domain.JavaClass.Predicates;
3436
import com.tngtech.archunit.core.domain.JavaClasses;
3537
import com.tngtech.archunit.core.domain.JavaMethod;
3638
import com.tngtech.archunit.core.domain.JavaParameter;
39+
import com.tngtech.archunit.core.domain.JavaType;
3740
import com.tngtech.archunit.core.domain.properties.CanBeAnnotated;
3841
import com.tngtech.archunit.core.importer.ClassFileImporter;
3942
import com.tngtech.archunit.lang.ArchCondition;
@@ -83,7 +86,8 @@ public ArchitectureCheck() {
8386
noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList(),
8487
noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding(),
8588
noClassesShouldCallStringToUpperCaseWithoutLocale(),
86-
noClassesShouldCallStringToLowerCaseWithoutLocale());
89+
noClassesShouldCallStringToLowerCaseWithoutLocale(),
90+
conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType());
8791
getRules().addAll(getProhibitObjectsRequireNonNull()
8892
.map((prohibit) -> prohibit ? noClassesShouldCallObjectsRequireNonNull() : Collections.emptyList()));
8993
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList()));
@@ -244,6 +248,36 @@ private List<ArchRule> noClassesShouldCallObjectsRequireNonNull() {
244248
.because("org.springframework.utils.Assert.notNull(Object, Supplier) should be used instead"));
245249
}
246250

251+
private ArchRule conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType() {
252+
return ArchRuleDefinition.methods()
253+
.that()
254+
.areAnnotatedWith("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean")
255+
.should(notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType())
256+
.allowEmptyShould(true);
257+
}
258+
259+
private ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType() {
260+
return new ArchCondition<>("not specify only a type that is the same as the method's return type") {
261+
262+
@Override
263+
public void check(JavaMethod item, ConditionEvents events) {
264+
JavaAnnotation<JavaMethod> conditional = item
265+
.getAnnotationOfType("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean");
266+
Map<String, Object> properties = conditional.getProperties();
267+
if (!properties.containsKey("type") && !properties.containsKey("name")) {
268+
conditional.get("value").ifPresent((value) -> {
269+
JavaType[] types = (JavaType[]) value;
270+
if (types.length == 1 && item.getReturnType().equals(types[0])) {
271+
events.add(SimpleConditionEvent.violated(item, conditional.getDescription()
272+
+ " should not specify only a value that is the same as the method's return type"));
273+
}
274+
});
275+
}
276+
}
277+
278+
};
279+
}
280+
247281
public void setClasses(FileCollection classes) {
248282
this.classes = classes;
249283
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ ExampleBean createExampleBean() {
506506
static class ConditionalOnIgnoredSubclass {
507507

508508
@Bean
509-
@ConditionalOnMissingBean(value = ExampleBean.class, ignored = CustomExampleBean.class)
509+
@ConditionalOnMissingBean(ignored = CustomExampleBean.class)
510510
ExampleBean exampleBean() {
511511
return new ExampleBean("test");
512512
}
@@ -517,7 +517,7 @@ ExampleBean exampleBean() {
517517
static class ConditionalOnIgnoredSubclassByName {
518518

519519
@Bean
520-
@ConditionalOnMissingBean(value = ExampleBean.class,
520+
@ConditionalOnMissingBean(
521521
ignoredType = "org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBeanTests$CustomExampleBean")
522522
ExampleBean exampleBean() {
523523
return new ExampleBean("test");
@@ -701,8 +701,7 @@ TestParameterizedContainer<CustomExampleBean> customExampleBean() {
701701
static class ParameterizedConditionWithValueConfig {
702702

703703
@Bean
704-
@ConditionalOnMissingBean(value = CustomExampleBean.class,
705-
parameterizedContainer = TestParameterizedContainer.class)
704+
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
706705
CustomExampleBean conditionalCustomExampleBean() {
707706
return new CustomExampleBean();
708707
}

0 commit comments

Comments
 (0)