- Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
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
- We have explicitly disabled bean overriding
- Had the bean (method) name been different (i.e.
Dependency beanDependency), we could have gotten theNoUniqueBeanDefinitionException
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.