Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autowiring does not work reliably in case of dynamically changing prototype bean class #26019

Closed
simonlei opened this issue Nov 3, 2020 · 2 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression
Milestone

Comments

@simonlei
Copy link

simonlei commented Nov 3, 2020

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.

demo.tar.gz

@wilkinsona
Copy link
Member

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.

@bclozel bclozel transferred this issue from spring-projects/spring-boot Nov 3, 2020
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Nov 3, 2020
@jhoeller jhoeller self-assigned this Nov 3, 2020
@jhoeller jhoeller added in: core Issues in core modules (aop, beans, core, context, expression) type: regression A bug that is also a regression and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Nov 3, 2020
@jhoeller jhoeller added this to the 5.3.1 milestone Nov 3, 2020
@jhoeller
Copy link
Contributor

jhoeller commented Nov 3, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

4 participants