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

mar*_*son 80 java spring cglib

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

例如,请参阅下面的代码(取自另一个问题).该entryPoint()方法用注释@Bean,所以我想春天会创建BasicAuthenticationEntryPoint一个bean 的新实例.然后,我们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();       
}
Run Code Online (Sandbox Code Playgroud)

was*_*ren 106

是的,Spring确实有些神奇.查看Spring文档:

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

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

@Beans 的默认范围是SINGLETON,如果指定不同的范围,例如PROTOTYPE调用将传递给原始方法.

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

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

  • @Nowhy ```CGLib 允许我们在运行时通过使用字节代码生成创建指定类的子类来创建代理类。CGLib 代理用于为那些没有任何接口或具有未在实现接口中声明的方法的类创建代理的情况。在这种情况下,CGLIB 创建 @Configuration 类的子类并覆盖它的方法(包括@Bean 方法)。因此,当我们从另一个方法调用 @Bean 方法时,我们实际上调用了它的覆盖版本(感谢 java 动态绑定)。 (4认同)
  • 但我还记得代理(jdk或CGLIB,无论哪个)都无法在自我调用中工作,那么@Configuration如何定义bean间依赖?它完全使用自我调用 (3认同)