Spring Autowired works before proxies are created
Asked Answered
F

1

6

As far as I understood, Spring manages autowiring mechanism with AutowiredAnnotationBeanPostProcessor on postProcessBeforeInitialization stage. But how does it inject proxies that ought to be created on postProcessAfterInitialization stage?

EDIT 1

Suppose I have this Spring configuration

@Service
class RegularBean {
    // injected on postProcessBeforeInitialization stage
    @Autowired
    private TransactionBean tBean;
    
    // invoked in between of postProcessBeforeInitialization and postProcessAfterInitialization
    @PostConstruct
    void init() {
        tBean.transactionMethod();
    }
}


@Service
class TransactionBean {
    // transactional proxy is created on postProcessAfterInitialization stage
    @Transactional
    public void transactionMethod() { ... }
}

Transactional proxy is created on postProcessAfterInitialization stage. But @PostConstruct is called right before it. Is injected tBean wrapped with transactional proxy? If it so, then why? Because it should not be. If it is not wrapped, then how transactions are going to be handled in the future?

Suppose that I replace field-injection with constructor-injection. Will it change the behavior somehow?

Feminize answered 17/3, 2021 at 20:5 Comment(0)
D
3

When you use autowiring on method or field ,Spring Container not always create and inject the required field/attribute instance. Spring internally create smart proxies and inject the proxies to your bean. This smart proxy will resolve the bean at later point and delegate call to actual bean during method invocation. This is a common strategy we use to resolve request and session scoped beans to singleton instance (Say service layer beans) using Scope annotation.

Adding small snippet to show Spring internally create proxies for Object. Consider a classic circular dependency case when we use Constructor injection.

@Component
public class CircularDependencyA {

    private CircularDependencyB circB;

    public CircularDependencyA(@Lazy CircularDependencyB circB) {
        System.out.println("CircularDependencyA Ctr ->"+circB.getClass().getName());
        this.circB = circB;
    }
}

@Component
public class CircularDependencyB {

    private CircularDependencyA circA;

    public CircularDependencyB(CircularDependencyA circA) {
        System.out.println("CircularDependencyB Ctr ->"+circA.getClass().getName());
        this.circA = circA;
    }
}

    @Configuration
    @ComponentScan(basePackages = { "com.example.springdemo.cd" })
    public class TestConfig {
    }

public class TestCircularDependency {

    public static void main(String[] args) {
        try(AnnotationConfigApplicationContext context 
                  = new AnnotationConfigApplicationContext(TestConfig.class);){
            
        }
    }

}

Based on our hints Spring is creating Proxy(using CGLIB) for CircularDependencyB Object and see the op from the CircularDependencyA Constructor CircularDependencyA Ctr ->com.example.springdemo.cd.CircularDependencyB$$EnhancerBySpringCGLIB$$e6be3b79 Thanks

Dimitri answered 1/3, 2022 at 11:18 Comment(6)
Does Spring always inject smart proxies via setters/fields? Is there any mention in the documentation? Because that's the first time I heard about it. By the way, what about constructor injection? Does Spring also apply smart proxies?Feminize
Attached a snippet. Run and see how Spring is passing a proxy at the constructor injection time. These proxies know how to resolve the actual beans at later point. So i like to call it smart proxies. You are getting the same bean in BeanPostProcessor -> postProcessBeforeInitializationDimitri
That's not exactly what I'm wondering. You're talking about lazy beans and circular dependencies. But I want to know how this "bean post processor stuff" works in general. I attached a code snippet to the question.Feminize
EnableTransactionManagement public class TestConfig { @Bean public PlatformTransactionManager transactionManager() { return new ResourcelessTransactionManager(); } }and see the type of TransactionBean in RegularBean the injected Object is a proxy not TransactionBean .So Spring can handle txn .In your snippet RegularBean will get a proxyDimitri
when RegularBean get TransactionBean. TransactionBean already completed postProcessBeforeInitialization and postProcessAfterInitialization but if you put init on TransactionBean It won't participate in any TXN ,Just act as normal method call.ie my understanding.Dimitri
So, it means that Spring injects only those beans that are already fully configured by all BeanPostProcessors? If there are some circular dependencies, then "smart proxies" come into play? Am I right?Feminize

© 2022 - 2024 — McMap. All rights reserved.