Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@ConditionalOn(Missing)Bean(annotation = …) infers the type to match when used on a @Bean method #42484

Closed
sdeeg opened this issue Sep 30, 2024 · 5 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@sdeeg
Copy link

sdeeg commented Sep 30, 2024

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

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 30, 2024
@wilkinsona
Copy link
Member

wilkinsona commented Sep 30, 2024

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:

* The condition can only match the bean definitions that have been processed by the
* application context so far and, as such, it is strongly recommended to use this
* condition on auto-configuration classes only. If a candidate bean may be created by
* another auto-configuration, make sure that the one using this condition runs after.

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.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Sep 30, 2024
@wilkinsona wilkinsona added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Sep 30, 2024
@wilkinsona
Copy link
Member

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:

* When placed on a {@code @Bean} method, the bean class defaults to the return type of
* the factory method:
*
* <pre class="code">
* &#064;Configuration
* public class MyAutoConfiguration {
*
* &#064;ConditionalOnBean
* &#064;Bean
* public MyService myService() {
* ...
* }
*
* }</pre>

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");
    }

}

@wilkinsona wilkinsona changed the title @ConditionalOnBean(annotation = XXX) doesn't work with only 1 bean registered @ConditionalOnBean(annotation = XXX) infers the type to match when used on a @Bean method Oct 2, 2024
@wilkinsona
Copy link
Member

wilkinsona commented Oct 2, 2024

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.

@wilkinsona wilkinsona reopened this Oct 2, 2024
@wilkinsona wilkinsona added status: waiting-for-triage An issue we've not yet triaged for: team-meeting An issue we'd like to discuss as a team to make progress and removed status: invalid An issue that we don't feel is valid labels Oct 2, 2024
@wilkinsona wilkinsona changed the title @ConditionalOnBean(annotation = XXX) infers the type to match when used on a @Bean method @ConditionalOnBean(annotation = …) infers the type to match when used on a @Bean method Oct 2, 2024
@philwebb philwebb added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged for: team-meeting An issue we'd like to discuss as a team to make progress labels Oct 2, 2024
@philwebb philwebb added this to the 3.4.x milestone Oct 2, 2024
@philwebb
Copy link
Member

philwebb commented Oct 2, 2024

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.

@wilkinsona wilkinsona changed the title @ConditionalOnBean(annotation = …) infers the type to match when used on a @Bean method @ConditionalOn(Missing)Bean(annotation = …) infers the type to match when used on a @Bean method Oct 2, 2024
@philwebb
Copy link
Member

philwebb commented Oct 2, 2024

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.

@philwebb philwebb self-assigned this Oct 2, 2024
@philwebb philwebb modified the milestones: 3.4.x, 3.4.0-RC1 Oct 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants