Skip to content

InjectionPoint not propagated by AutowiredAnnotationBeanPostProcessor's cached argument resolution [SPR-14400] #18971

Closed
@spring-projects-issues

Description

@spring-projects-issues

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:

Issue Links:

Referenced from: commits e15f7ef

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions