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

Posted

技术标签:

【中文标题】在 Spring java 配置中调用 @Bean 注解的方法【英文标题】:Calling a @Bean annotated method in Spring java configuration 【发布时间】:2015-03-15 10:01:54 【问题描述】:

我很好奇spring注入如何处理带有@Bean注解的调用方法。如果我在方法上添加@Bean 注释并返回一个实例,我知道这会告诉spring 通过调用该方法并获取返回的实例来创建一个bean。但是,有时该 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】:

是的,Spring 确实有一些魔法。检查Spring Docs:

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

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

@Beans 的默认作用域是SINGLETON,如果您指定不同的作用域,例如PROTOTYPE,调用将被传递给原始方法。

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

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

【讨论】:

是否可以覆盖以这种方式创建的 bean?例如,我有一个 Spring 定义的类,它直接调用一个 bean 创建方法。我想要的是不使用由该方法创建的 bean,而是使用我自己定义的 bean(通过使用 @Bean@Primary 对其进行注释)。 但我也记得代理(jdk 或CGLIB,无论哪个)不能在自调用中工作,那么@Configuration 是如何定义bean 间依赖的呢?它完全使用自调用 @Nowhy CGLib allows us to create proxy classes at runtime by creating sub class of specified class using Byte code generation. CGLib proxies are used in the case where Proxy is to be created for those class which does not have any interfaces or have methods which are not declared in the implementing interface. 在这种情况下,CGLIB 创建@Configuration 类的子类并覆盖其方法(包括@Bean 方法)。因此,当我们从另一个方法调用 @Bean 方法时,我们实际上调用了它的覆盖版本(感谢 java 动态绑定)。 如果我使用 CHLIB 而不是 java Poxy 创建代理,@Component 中的 selfInvocation AOP 会起作用吗?

以上是关于在 Spring java 配置中调用 @Bean 注解的方法的主要内容,如果未能解决你的问题,请参考以下文章

如何接收配置的bean的返回值

Spring配置中的bean直接引用其它bean的属性值

Java后台代码调用Spring的@Service Bean的方式

Java小节,spring源码分析

Spring的bean配置

Spring装配bean(在java中进行显式配置)