一尘不染

在Spring Java配置中调用@Bean带注释的方法

spring

我很好奇弹簧注入如何处理带有@Bean注释的调用方法。如果我@Bean在方法上添加注释并返回实例,则我理解这告诉spring通过调用方法并获取返回的实例来创建bean。但是,有时必须使用该bean来连接其他bean或设置其他代码。完成此操作的通常方法是调用带@Bean注释的方法以获取实例。我的问题是,为什么这不会导致有多个bean实例漂浮?

例如,请参见下面的代码(取自另一个问题)。该entryPoint()方法带有注释@Bean,因此我可以想象spring将创建一个BasicAuthenticationEntryPointas的新实例。然后,我们entryPoint()configure块中再次调用,但是似乎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();       
}

阅读 1800

收藏
2020-04-12

共1个答案

一尘不染

是的,Spring做些魔术。查看Spring Docs:

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

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

@Beans的默认范围是SINGLETON,如果你指定其他范围,例如PROTOTYPE调用将传递给原始方法。

请注意,这对于静态方法无效。根据spring文件:

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

2020-04-12