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

标签 spring spring-aop

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

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

那么,在下面的代码中,Spring 是否注入(inject)了 SimpleBean 的实际实例或代理?

@Configuration
public class Config {

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

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

如果一个类用@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 两次调用 simpleBean() 怎么可能返回同一个实例简单的 bean(因为它显然是一个单例)?

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

正如您所说,现在确实 Spring AOP 有调用方法的限制,但是谁说 spring under-the-hood 使用 spring AOP?在低得多的级别上完成的字节码检测没有这样的限制。毕竟创建代理意味着:“创建一个具有相同接口(interface)但会改变行为的代理对象”,对吧?

例如,如果您使用使用继承的 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;    
         }
     }
     ....
}

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

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

举个例子: 如果您使用 @ConditionalOnClass(A.class) 并且该类在运行时并不真正存在,那么 spring 如何加载使用此配置的配置的字节码并且不会在类似 的情况下失败NoClassDefFoundException?

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

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

关于使用@Configuration 或@Component 注释的类的Spring 代理创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56625024/

相关文章:

java - 具有匿名内部类的 Spring AOP

java - 当我尝试使用切入点和通配符表达式创建 Bean 时,为什么会出现错误?

spring - 使用 Tomcat、Spring 和 Weaver (BcelClassWeaver) 启动期间出现异常

java - 未执行的方面

Spring 执行器 "/actuator"端点不可用

java - StompSessionHandler 不在 afterConnected block 中调用 handleFrame?

配置服务器上的 spring-cloud-starter-bus-kafka 设置

java - 使用 Spring AOP 清理记录器

java - jackson 无视@JsonIgnore?

java - Spring Elasticsearch HashMap[String, String] 映射值不能not_analyzed