在Spring java配置中调用@Bean注释方法

时间:2015-01-16 18:04:04

标签: java spring cglib

我很好奇Spring注入如何处理使用@Bean注释调用方法。如果我在方法上放置一个@Bean注释,并返回一个实例,我理解这会告诉spring通过调用方法并获取返回的实例来创建bean。但是,有时该bean必须用于连接其他bean或设置其他代码。这样做的通常方法是调用@Bean带注释的方法来获取实例。我的问题是,为什么这不会导致豆子的多个实例浮动?

例如,请参阅下面的代码(取自另一个问题)。 entryPoint()方法使用@Bean注释,因此我认为spring将创建一个BasicAuthenticationEntryPoint的新实例作为bean。然后,我们在配置块中再次调用entryPoint(),但似乎entryPoint()返回bean实例,并且没有被多次调用(我尝试记录,只有一个日志条目) 。我们可能会在配置的其他部分多次调用entryPoint(),并且我们总是会获得相同的实例。我对此的理解是否正确? Spring是否会对使用@Bean注释的方法进行一些神奇的重写?

@Bean
public BasicAuthenticationEntryPoint entryPoint() {
    BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
    basicAuthEntryPoint.setRealmName("My Realm");
    return basicAuthEntryPoint;
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http
        .exceptionHandling()
            .authenticationEntryPoint(entryPoint())
            .and()
        .authorizeUrls()
            .anyRequest().authenticated()
            .and()
        .httpBasic();       
}

1 个答案:

答案 0 :(得分:98)

是的,Spring会做一些魔法。查看Spring Docs

  

这就是魔术的用武之地:所有@Configuration类在启动时都使用CGLIB进行子类化。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器是否有任何缓存(作用域)bean。

这意味着对@Bean方法的调用是通过CGLIB代理的,因此返回了bean的缓存版本(不会创建新版本)。

@Bean的默认范围是SINGLETON,如果您指定了不同的范围,例如PROTOTYPE,则调用将传递给原始方法。

请注意,对静态方法无效。根据春季文档:

  

由于技术限制,对静态@Bean方法的调用永远不会被容器截获,甚至不在@Configuration类中(如本节前面所述):CGLIB子类化只能覆盖非静态方法。因此,直接调用另一个@Bean方法具有标准的Java语义,从而导致直接从工厂方法本身返回一个独立的实例。