I am using springboot 2.3.5.RELEASE, have one BeanFactory to load prototype beans implements one interface. One has a bean to be autowired, another doesn't have the bean.
@Bean
@Scope(value = "prototype")
public DemoInterface getDemoBean( int i) {
switch ( i) {
case 1: return new DemoClassOne();
case 2:
default:
return new DemoClassTwo();
}
}
public class DemoClassOne extends AbstractDemoClass {
@Autowired
Bean2BeAutowired bean2BeAutowired;
}
public class DemoClassTwo extends AbstractDemoClass {
}
If I load BeanOne first, the bean2BeAutowired works fine, but after I load another bean, then generate bean one, the bean2BeAutowired will be null.
@Test
void contextLoads() {
DemoInterface a1 = ctx.getBean(DemoInterface.class, 1);
System.out.println( a1);
System.out.println( "bean2BeAutowired is ok: "+((DemoClassOne)a1).bean2BeAutowired);
DemoInterface a2 = ctx.getBean( DemoInterface.class, 2);
System.out.println( a2);
DemoInterface a3 = ctx.getBean( DemoInterface.class, 1);
// ctx.getAutowireCapableBeanFactory().autowireBean( a1);
System.out.println( a3);
System.out.println( "bean2BeAutowired is null: "+((DemoClassOne)a3).bean2BeAutowired);
}
The output is:
com.example.demo.DemoClassOne@7561db12
bean2BeAutowired is ok: com.example.demo.Bean2BeAutowired@3301500b
com.example.demo.DemoClassTwo@15deb1dc
com.example.demo.DemoClassOne@6e9c413e
bean2BeAutowired is null: null
It means after I load bean2, the bean1 will not autowired bean2BeAutowired. The demo code is attached.
Comment From: wilkinsona
Thanks for the sample. It's a bit more complicated than it needs to be to reproduce the problem. Your subclass of DefaultListableBeanFactory
is also quire unusual and isn't necessary. Here's a single file variant that has the same behaviour while removing the use of Spring Boot and the custom BeanFactory
:
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = DemoApplication.class)
class DemoApplicationTests {
@Autowired
ApplicationContext ctx;
@Test
void contextLoads() {
DemoInterface a1 = ctx.getBean(DemoInterface.class, 1);
System.out.println( a1);
System.out.println( "bean2BeAutowired is ok: "+((DemoClassOne)a1).bean2BeAutowired);
DemoInterface a2 = ctx.getBean( DemoInterface.class, 2);
System.out.println( a2);
DemoInterface a3 = ctx.getBean( DemoInterface.class, 1);
// ctx.getAutowireCapableBeanFactory().autowireBean( a1);
System.out.println( a3);
System.out.println( "bean2BeAutowired is null: "+((DemoClassOne)a3).bean2BeAutowired);
}
}
class DemoClassOne extends AbstractDemoClass {
@Autowired
Bean2BeAutowired bean2BeAutowired;
}
class DemoClassTwo extends AbstractDemoClass {
}
interface DemoInterface {
}
class AbstractDemoClass implements DemoInterface {
}
@Configuration
@ComponentScan
class DemoApplication {
@Bean
@Scope(value = "prototype")
public DemoInterface getDemoBean( int i) {
switch ( i) {
case 1: return new DemoClassOne();
case 2:
default:
return new DemoClassTwo();
}
}
}
@Service
class Bean2BeAutowired {
}
As the problem occurs without Spring Boot's involvement, we'll transfer this to the Framework team so that they can take a look.
Comment From: jhoeller
This seems to be (yet another) regression caused by the InjectionMetadata.EMPTY
optimization in 5.2... in this particular case, caused by the previous fix #24485. I'll refine this once more for 5.3.1 and 5.2.11, hopefully for good this time.