为什么在Spring中我不允许使用@Configuration注释最终类?

And*_*ili 10 java spring annotations spring-mvc spring-annotations

我正在攻读Spring Core认证,我对这个基于学习材料的问题的答案有些怀疑.

为什么不允许使用@Configuration注释最终类

我的理由是以下一个证实了这个断言:

请考虑以下配置类:

@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());
}
Run Code Online (Sandbox Code Playgroud)

首先看这种情况可能看起来很奇怪,因为第一个方法(accountRepository())将一个JdbcAccountRepository对象实例化为一个具有id = AccountRepository的bean ,在Spring默认行为之后,它是一个单例

第二个和第三个方法再调用两次以上accountRepository()方法,它应该再实例化两次JdbcAccountRepository对象,这是不可能的,因为它是单例!

因此,为了解决这种情况,Spring使用基于继承的代理策略,该策略期望创建我的配置类的子类(由@Configuration注释的子类),它确实如下:

  • 对于每个bean,实例都缓存在子类中

  • 子类只在第一次实例化时调用super

因此子类是入口点,因为此子类实现了以下行为:

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
}

.....................................................
.....................................................
.....................................................
}
Run Code Online (Sandbox Code Playgroud)

因此,如果我使用最终的 Spring 注释配置类,则不能有这种行为,因为在Java中,最终的类不能被子类化

这是对的吗?

使用相同的推理我还可以断言在Spring中我不能用@Bean注释注释最终方法吗?

因为,如前面的例子所示,我在创建启动时创建了配置类的子类(代理),对于每个bean,实例都缓存在子类中,如果它是最终的,则它是不可能(但我绝对不确定这个断言)

我错过了什么吗?你能给我一个确切的解释吗?

TNX

Mit*_*hun 9

Spring为使用类注释的@Configuration类创建动态代理.Spring使用CGLIB扩展您的类以创建代理.因此,配置类不能是最终的.

关于accountRepository()被调用两次:

如果您调用accountRepository()方法来创建实例,它就不再是Spring托管bean.Spring不会知道以这种方式创建的实例.因此,您将最终得到多个实例JdbcAccountRepository

如果配置如下,则可以保留单例行为:

@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);
} 
Run Code Online (Sandbox Code Playgroud)

  • 即使它有点晚了,但我只是偶然发现了这个答案:当然Spring无法看到accountRepository()方法中发生了什么.但Spring代理将拦截对accountRepository()方法本身的所有调用,并检查是否已在当前应用程序上下文中定义了该类型和名称的bean.如果是这种情况,代理返回bean(并且不执行该方法),否则,调用真实方法并将返回的bean存储在应用程序上下文中.这就是为什么在配置bean中多次调用bean创建方法是安全的 (3认同)
  • 实际上,如果子类正常工作,您可以调用其他bean方法(如accountRepository()).然后,代理将确定是否有必要创建新的bean实例或者是否已存在. (2认同)