Spring 使用 @Configuration 或 @Component 注释的 类 的代理创建

Spring Proxy Creation of Classes annotated with @Configuration or @Component

Spring 使用 JDK 动态代理或 CGLIB 为给定的目标对象创建代理。如果 class 被 @Configuration 注释,则使用 CGLIB。

但是,Spring AOP 的一个限制是,一旦调用最终到达目标对象,它可能对自身进行的任何方法调用都将针对 this 引用进行调用,而不是针对代理。在使用 @Transactional 和其他地方时,记住这条信息很重要。

那么在下面的代码中,有了这些知识,Spring 就会注入 SimpleBean 的实际实例或代理?

@Configuration
public class Config {

@Bean
public SimpleBean simpleBean() {
    return new SimpleBean();
}

@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
    return new SimpleBeanConsumer(simpleBean()); //<---
}
}

如果 class 是带有 @Component 的注释,行为是什么?

让我给你换个角度。

假设有另一个 bean AnotherBeanConsumer 也需要一个 simpleBean。 Simple Bean 有一个 Singleton 作用域:

 @Configuration
 public class Config {
    @Bean
    public SimpleBean simpleBean() {
       return new SimpleBean();
    }

    @Bean
    public SimpleBeanConsumer simpleBeanConsumer() {
       return new SimpleBeanConsumer(simpleBean());
    }

    @Bean
    public AnotherBeanConsumer anotherBeanConsumer() {
       return new AnotherBeanConsumer(simpleBean());
    }        
 }

现在的问题是,从不同的方法 simpleBeanConsumeranotherBeanConsumer return 对 simpleBean() 的两次调用如何可能是同一个简单 bean 的实例(因为它显然是单身人士)?

IMO(免责声明,我不隶属于 spring 或其他组织),这是创建包装配置的代理的主要原因。

现在Spring AOP确实有调用方法的限制,就像你说的那样,但是谁说spring under-the-hood使用spring AOP?在低得多的级别上完成的字节码检测没有这样的限制。毕竟创建代理意味着:"create a proxy object that will have the same interface but will alter the behavior",对吧?

例如,如果您使用使用继承的 CGLIB,您可以从配置中创建一个代理,如下所示(示意图):

class CGLIB_GENERATED_PROXY extends Config {

     private Map<String, Object> singletonBeans;

     public SimpleBean simpleBean() {
         String name = getNameFromMethodNameMaybePrecached();
         if(singletonBeans.get(name) != null) {
            return singletonBeans.get(name);
         }  
         else {
            SimpleBean bean = super.simpleBean(); 
            singletonBeans.put(name, bean);
            return bean;    
         }
     }
     ....
}

当然这只是一个示意图,在现实生活中有一个应用上下文基本上提供了这样的地图访问,但你明白了。

如果这还不够,那么 spring 必须使用一些更复杂的框架来加载配置(如 ASM)...

这是一个例子: 如果您使用 @ConditionalOnClass(A.class) 并且 class 在运行时并不存在,那么 spring 如何加载使用此配置的配置的字节码并且不会在类似 [=18= 的情况下失败]?

我的观点是它远远超出了 spring AOP,并且有其怪癖 :)

话虽如此,我上面描述的任何内容都不需要真正的组件始终 包裹在任何类型的代理中。所以在最简单的情况下,当 SimpleBean 本身没有一些需要代理生成的注释(像 @Cached@Transactional 等等)时,Spring 不会如果不包装该类型的对象,您将得到一个普通的 SimpleBean 对象。