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

Unexpected bean override / inject behavior #32825

Open
gavlyukovskiy opened this issue May 14, 2024 · 0 comments
Open

Unexpected bean override / inject behavior #32825

gavlyukovskiy opened this issue May 14, 2024 · 0 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@gavlyukovskiy
Copy link

gavlyukovskiy commented May 14, 2024

Affects: 6.1.x, partly 5.3.x, 6.0.x


Spring Framework 6.1 introduced a change to allow overriding auto-scanned beans silently as those are guaranteed to be equivalent, the issue and the solution completely makes sense for the problem it solved, but we have found a small problem with that behavior.

This change allows to subtly override the behavior from a @Configuration class in a case like this:

@Component
public class Dependency {
    @Autowired
    public Dependency(ApplicationContext context) { // context as a placeholder dependency
        this(context, "from @Component");
    }
    public Dependency(ApplicationContext context, String origin) {
        System.out.println(origin);
    }
}

the stereotype annotation indicates that the first constructor will be called, but it can be overridden by using explicit configuration:

@Bean
public Dependency dependency(ApplicationContext context) {
    return new Dependency(context, "from @Bean");
}

(the code prints from @Bean, before 6.1 results in BeanDefinitionOverrideException)

This problem isn't as severe since both declarations are, without a doubt, explicit, however it is slightly unexpected because

  1. We have explicitly disabled bean overriding
  2. Had the bean (method) name been different (i.e. Dependency beanDependency), we could have gotten the NoUniqueBeanDefinitionException

Another somewhat related to unexpected bean overriding (but likely different :)), is the fact that Spring uses the field name at the injection point as a qualifier in case of conflicts, which also creates the same problem when used together with FullyQualifiedAnnotationBeanNameGenerator. In this case, we would have 2 distinct beans named dependency and com.example.Dependency, but since most code injecting the bean looks like this

@Service
public class MyService {
    private final Dependency dependency;
    public MyService(Dependency dependency) {
        this.dependency = dependency;
    }
}

The added configuration effectively results in bean overriding for all the dependents (this also affects Spring before 6.1). In regards to this one, I wonder if you'd recommend doing this:

beanFactory.setParameterNameDiscoverer(null)

to disable the field name matching (so that we require explicit @Qualifier) or can it have some unexpected consequences?

For some context: these issues might look quite synthetic, but we're encountering these on a monthly basis while maintaining a large mono-repository with around 3,000 shared modules, many of which declare spring beans (99.99% rely on auto-scanned bean definitions) and having a configuration is some rogue module often overrides the bean for everyone else.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 14, 2024
@jhoeller jhoeller added the in: core Issues in core modules (aop, beans, core, context, expression) label May 14, 2024
@jhoeller jhoeller self-assigned this May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

3 participants