Why in Spring I am not allowed to annotate a final class with @Configuration?

10,084

Spring creates dynamic proxies for classes annotated with @Configuration classes. Spring uses CGLIB to extend your class to create proxy. Hence, configuration classes cannot be final.

Regarding accountRepository() being invoked twice:

If you invoke accountRepository() method to create an instance, it is no more a Spring managed bean. Spring will not have any idea of the instances created in this manner. Hence, you will end up with multiple instances of JdbcAccountRepository

You can preserve the singleton behavior if you configure as below:

@Bean
public TransferService transferService(JdbcAccountRepository jdbcAcctRepo) {
    TransferServiceImpl service = new TransferServiceImpl();
    service.setAccountRepository(jdbcAcctRepo);
    return service;
}

@Bean
public AccountService accountService(JdbcAccountRepository jdbcAcctRepo) {
    return new AccountServiceImpl(jdbcAcctRepo);
} 
Share:
10,084

Related videos on Youtube

AndreaNobili
Author by

AndreaNobili

Updated on September 16, 2022

Comments

  • AndreaNobili
    AndreaNobili over 1 year

    I am studying for the Spring Core certification and I have some doubts related to the answer of this question founded on the study material stuff.

    Why are you not allowed to annotate a final class with @Configuration

    My reasoning is the following one for substantiate this assertion:

    Consider the following configuration class:

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(); 
    }
    
    @Bean
    public TransferService transferService() {
        TransferServiceImpl service = new TransferServiceImpl();
        service.setAccountRepository(accountRepository());
        return service;
    }
    
    @Bean
    public AccountService accountService() {
        return new AccountServiceImpl(accountRepository());
    }
    

    At first look this situation could appear strange because the first method (accountRepository()) instantiates an JdbcAccountRepository object as a bean having id=AccountRepository that, following the Spring default behavior, is a singleton

    The second and the third method call twice more time the accountRepository() method that should instantiate twice more JdbcAccountRepository objects and this is not possibile because it is singleton !!!

    So, to solve this situation Spring use the Inheritance-based Proxies strategy that expect to create a child class of my configuration class (the one annoted by @Configuration) and it is does:

    • For each bean, an instance is cached in the child class

    • Child class only calls super at first instantiation

    So the child class is the entry point because the following behavior is implemented by this child class:

    public class AppConfig$$EnhancerByCGLIB$ extends AppConfig {

    public AccountRepository accountRepository() {
        // if bean is in the applicationContext
        // return bean
        // else call super.accountRepository() and store bean in context
    }
    
    public TransferService transferService() {
        // if bean is in the applicationContext, return bean
        // else call super.transferService() and store bean in context
    }
    
    .....................................................
    .....................................................
    .....................................................
    }
    

    So if I annotate a configuration class with final Spring can't have this behavior because in Java a final class cannot be subclassed

    Is it correct?

    Using the same reasoning can I also assert that in Spring I can't have a final method annoted with @Bean annotation?

    Because, as shown in the previous example, I have that when at startup time is created the child class (the proxy) of my configuration class happens that for each bean, an instance is cached in the child class and if it is final it is not possible (but I am absolutly not sure about this assertion)

    Am I missing something? Can you give me the exact explaination?

    Tnx

  • dunni
    dunni about 9 years
    Actually if the subclassing works correctly, you can call other bean methods (like accountRepository()). The proxy will then determine if it is necessary to create a new bean instance or if one already existrs.
  • Mithun
    Mithun about 9 years
    If the configuration class is not final, Spring can sub-class. And, related to the accountRepository() method invocation, Spring doesn't know what is happening inside the method. Spring cannot hook in a logic to check if the bean is already created, each time the new operator is used.
  • dunni
    dunni over 8 years
    Even it's a bit late, but i just stumbled again over this answer: Of course Spring cannot see what happens in the accountRepository() method. But the Spring proxy will intercept all calls to the accountRepository() method itself, and checks, if there is already a bean of that type and name defined in the current application context. If this is the case, the proxy returns the bean (and doesn't execute the method), if not, the real method is called and the returned bean is stored in the application context. That's why it's safe to call bean creation methods multiple times in a configuration bean
  • Irfan Bhindawala
    Irfan Bhindawala over 5 years
    @dunni, you are absolutely right, @Mithun, Please refer java doc for @Bean annotation (methods in @Configuration annotated classes section of doc)