Description
Sebastian Staack opened SPR-14400 and commented
When there is a prototype bean with a field annotated with @Autowired
whose value is a prototype bean created in dependence of its injection point it seams that the the injection point isn't propagated.
Test setup:
public class MyBeanImpl implements MyBean {
@Autowired
private Logger logger;
@PostConstruct
public void init() {
logger.info("Hello World");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyTest {
@Configuration
static class TestConfiguration {
@Bean
@Scope(SCOPE_PROTOTYPE)
public Logger createLogger(InjectionPoint injectionPoint) {
return LogManager.getLogger(injectionPoint.getMember().getDeclaringClass());
}
@Bean
@Scope(SCOPE_PROTOTYPE)
public MyBean createMyBean() {
return new MyBeanImpl();
}
}
@Autowired
private MyBean beanA;
@Autowired
private MyBean beanB;
@Test
public void test() {
}
public static void main(String[] args) {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestConfiguration.class)) {
ctx.getBean(MyBean.class);
ctx.getBean(MyBean.class);
}
}
}
If I execute the test then I get the following log:
16:36:53.997 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [test.MyTest]: no resource found for suffixes {-context.xml}.
16:36:54.092 [main] INFO org.springframework.test.context.support.AbstractDelegatingSmartContextLoader - AnnotationConfigContextLoader detected default configuration classes for context configuration [ContextConfigurationAttributes@255b53dc declaringClass = 'test.MyTest', classes = '{class test.MyTest$TestConfiguration}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
16:36:54.105 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
16:36:54.109 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
16:36:54.110 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
16:36:54.112 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
16:36:54.112 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6295d394, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@475e586c, org.springframework.test.context.support.DirtiesContextTestExecutionListener@657c8ad9]16:36:54.302 [main] INFO org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@27ae2fd0: startup date [Sat Jun 25 16:36:54 CEST 2016]; root of context hierarchy
16:36:54.757 [main] INFO test.MyBeanImpl - Hello World
16:36:54.758 [main] INFO test.MyTest - Hello World
16:36:54.764 [Thread-1] INFO org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@27ae2fd0: startup date [Sat Jun 25 16:36:54 CEST 2016]; root of context hierarchy
The lines 16:36:54.757 and 16:36:54.758 in the log show that the InjectionPoint of the Logger field differs, because the injection point's member declaring class is used to instantiate the logger. I would expect that they don't differ because both times the same bean is instantiated.
After a closer look I recognized that the second logger is instantiated for the test class MyTest, so I assume that the injection point isn't populated recursively. To prove my assumption I created the method MyTest#main()
where I setup an ApplicationContext
by hand. When I execute this method I get the following log:
16:56:43.372 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@436e852b: startup date [Sat Jun 25 16:56:43 CEST 2016]; root of context hierarchy
16:56:44.071 [main] INFO test.MyBeanImpl - Hello World
16:56:44.077 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@436e852b: startup date [Sat Jun 25 16:56:43 CEST 2016]; root of context hierarchy
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'createMyBean': Injection of autowired dependencies failed; nested exception is java.lang.IllegalStateException: No current InjectionPoint available for method 'createLogger' parameter 0
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:356)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:325)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:220)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:352)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1088)
at test.MyTest.main(MyTest.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.IllegalStateException: No current InjectionPoint available for method 'createLogger' parameter 0
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:830)
at org.springframework.beans.factory.support.ConstructorResolver.resolvePreparedArguments(ConstructorResolver.java:784)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:415)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:325)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.resolvedCachedArgument(AutowiredAnnotationBeanPostProcessor.java:533)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.access$200(AutowiredAnnotationBeanPostProcessor.java:118)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:562)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:350)
... 14 more
This log confirmed my assumption from above, because now there isn't a parent injection point and the thrown error state that there isn't an current injection point at all. I guess the that the method AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject
must be fixed to populate the injection point of its field via ConstructorResolver#setCurrentInjectionPoint()
.
I have attached a maven project so that you are able to reproduce the observed behavior.
Affects: 4.3 GA
Attachments:
- spring-injectionpoint-bug.zip (31.30 kB)
Issue Links:
- Documentation for InjectionPoint argument on @Bean method [SPR-14797] #19363 Documentation for InjectionPoint argument on
@Bean
method
Referenced from: commits e15f7ef