Reproduced in Spring 5.2.7.RELEASE (Spring Boot 2.3.1.RELEASE):
@Configuration
public class SomeConfig {
@Bean(name = "other")
SomeOtherBean foo() {
System.out.println("constructing SomeOtherBean");
return new SomeOtherBean();
}
@Bean(name = "foo")
SomeBean foo(@Qualifier("other") SomeOtherBean other) {
System.out.println("constructing SomeBean");
return new SomeBean(other);
}
}
With the above configuration class, SomeOtherBean is constructed twice, and SomeBean is never constructed. Why? Because the two methods have the same name. If you rename the second method to bar, everything is constructed once, as expected.
Admittedly, using the same method name is not a good idea. I just happened to have done it by accident.
Comment From: lifejwang11
Reproduced in Spring 5.2.7.RELEASE (Spring Boot 2.3.1.RELEASE):
``` @Configuration public class SomeConfig { @Bean(name = "other") SomeOtherBean foo() { System.out.println("constructing SomeOtherBean"); return new SomeOtherBean(); }
@Bean(name = "foo") SomeBean foo(@Qualifier("other") SomeOtherBean other) { System.out.println("constructing SomeBean"); return new SomeBean(other); }} ```
With the above configuration class,
SomeOtherBeanis constructed twice, andSomeBeanis never constructed. Why? Because the two methods have the same name. If you rename the second method tobar, everything is constructed once, as expected. Admittedly, using the same method name is not a good idea. I just happened to have done it by accident.
hello,I think you might need to provide a demo,Because I don't know the relationship between the two,Thanks.
Comment From: jnizet
@lifejwang11 not sure exactly what you mean by "the relationship between the two". If you're talking about the two beans SomeBean and SomeOtherBean, they're irrelevant. Spring should construct an instance of each of them by calling each of the @Bean methods once, but does not, and instead calls the first one twice, and never calls the second one.
But anyway, here's a complete demo if you want to try it by yourself: https://github.com/jnizet/spring-overload-issue
Comment From: sbrannen
If I execute the following test against master ...
@SpringJUnitConfig
class SomeConfigTests {
@Test
void test() {
}
@Configuration
static class SomeConfig {
@Bean(name = "other")
public SomeOtherBean foo() {
System.out.println("constructing SomeOtherBean");
return new SomeOtherBean();
}
@Bean(name = "foo")
public SomeBean foo(@Qualifier("other") SomeOtherBean other) {
System.out.println("constructing SomeBean");
return new SomeBean(other);
}
}
}
The output is:
constructing SomeOtherBean
constructing SomeBean
And the output is the same for the 5.2.x branch as well as the v5.2.7.RELEASE release tag.
Though, I can confirm that the output from your sample Spring Boot application is the following.
constructing SomeOtherBean
constructing SomeOtherBean
Comment From: sbrannen
Since I have not been able to reproduce this in Spring Framework 5.2.x or master, I'm wondering if the behavior is specific to Spring Boot.
@philwebb and @snicoll, would you be willing to look at this from the perspective of Spring Boot?
Comment From: wilkinsona
The unit test isn't functionally equivalent to the sample as you have removed component scanning from the equation. The sample application still reproduces the problem when modified to remove any use of Spring Boot:
package com.example.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class DemoApplication {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(DemoApplication.class);
}
}
Comment From: sbrannen
Tentatively slated for 5.3 M2 for further investigation
Comment From: lifejwang11
Tentatively slated for 5.3 M2 for further investigation
I think the key to this question is AbstractAutowireCapableBeanFactory#getTypeForFactoryMethod
for (Method candidate : candidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
candidate.getParameterCount() >= minNrOfArgs) {
// doSmonething
}
}
If you have two methods with the same name and bean annotation in the same class,I think you need to determine if beanName is the same,so I think this code should be modified in ConfigurationClassBeanDefinitionReader#isFactoryMethod
old
@Override
public boolean isFactoryMethod(Method candidate) {
return (super.isFactoryMethod(candidate) && BeanAnnotationHelper.isBeanAnnotated(candidate));
}
new
@Override
public boolean isFactoryMethod(Method candidate) {
return (super.isFactoryMethod(candidate) && BeanAnnotationHelper.isBeanAnnotated(candidate) && BeanAnnotationHelper.determineBeanNameFor(candidate).equals(candidate.getName()));
}
If my idea is right, I would like to submit a PR,thanks
Comment From: lifejwang11
@sbrannen The bug I think Methods with @bean should not only determine if the method name is the same, but also if the name attribute corresponding to the bean annotation of the method (or the default method name without the name attribute) is the same as the current method name,I think the way to do bean annotation is like this, don't you think
Comment From: kse-music
in spring framework 5.2.8 also exist this issue
Comment From: jhoeller
I've tracked this down to a difference between Class-based processing of the configuration class versus ASM-based processing against a registered configuration class name. A variant of the unit test passes with Class registration (similar to the @SpringJUnitConfig scenario above) but fails with name-based registration (similar to component scanning).