Bug report
We’ve been running into an issue with the @ConditionalOnBean(annotation = XXX) annotation with Spring Boot. It seems that the conditional doesn’t work if the bean with the conditional is the only bean of that type being registered. Attached is a sample project to demonstrate.
spring-boot-conditional-annotation.tar.gz
Comment From: wilkinsona
For your conditional to work, it has to be evaluated after foo has been defined but that may not be the case as you've defined both it and bar2 in the same @Configuration class. You need to guarantee that things are processed in the required order, as noted in the javadoc of @ConditionalOnBean:
https://github.com/spring-projects/spring-boot/blob/689bce88d2832c730eb75ceeeb2adb8270e983a4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBean.java#L56-L59
You should move the definition of bar2 into an @AutoConfiguration class that's registered through an org.springframework.boot.autoconfigure.AutoConfiguration.imports file. Note that auto-configuration classes should be in a package that isn't covered by the application's component scanning.
Comment From: wilkinsona
There was a little bit more to this due to the condition being used on a @Bean method:
@ConditionalOnBean(annotation = MyAttribute.class)
@Bean
public Bar bar() {
return new Bar("stuff");
}
This usage means that the following applies:
https://github.com/spring-projects/spring-boot/blob/99142dbbb2d4d3454051e49ec1aeab7b62991990/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBean.java#L38-L51
As a result, the condition was looking for a bean of type Bar that is annotated with @MyAttribute.
To look for a bean of any type that is annotated with @MyAttribute, the condition should be used at the class level. For example:
package com.example.demo.lib;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@AutoConfiguration
@ConditionalOnBean(annotation = MyAttribute.class)
public class MyAutoConfiguration {
@Bean
public Bar bar() {
return new Bar("stuff");
}
}
Comment From: wilkinsona
Re-opening as there's more to this still.
When used on a @Bean method and you specify a type (using either name or value) or name to match, the type to match is no longer inferred from the return type of the method. However, when you specific an annotation to match, that inference is still performed. It's been this way since the annotation-based matching was added but after a nudge from @sdeeg, I'm doubtful that this is intentional and may in fact be a bug.
We could change the behavior by skipping the inference when annotation is specified but this may break some existing usages of the condition. Restoring the old behavior would require using the type attribute to explicitly declare the bean method's return type as part of the matching spec so this may be appropriate for a new minor but feels a little risky for a maintenance release.
I'd like to discuss this with the rest of the team and also consider what, if anything, we should do for @ConditionalOnMissing bean which currently behaves in the same way.
Comment From: philwebb
We discussed this today and we think this is a bug, but one that's too risky for a patch release. We're going to remove the inference entirely for @ConditionalOnBean in 3.4. At that time, we'll also remove the inference when using the annotation attribute on @ConditionalOnMissingBean.
Comment From: philwebb
We're going to remove the inference entirely for @ConditionalOnBean in 3.4.
In hindsight, I'm not sure we should do this part. It's pretty unlikely, but possible that a user wants to create two beans of the same type when one is present.