ChatGPT解决这个技术问题 Extra ChatGPT

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

我很好奇 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();       
}

J
Julian Cardenas

是的,Spring 做了一些魔法。检查 Spring Docs

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

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

@Bean 的默认范围是 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 会起作用吗?