【问题标题】:Spring Proxy Creation of Classes annotated with @Configuration or @Component使用 @Configuration 或 @Component 注释的类的 Spring 代理创建
【发布时间】:2019-06-17 14:03:28
【问题描述】:

Spring 使用 JDK 动态代理或 CGLIB 为给定的目标对象创建代理。如果一个类使用@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()); //<---
}
}

如果一个类被@Component注解,会有什么行为?

【问题讨论】:

  • 应该差不多。
  • @EdwardAung 抱歉我没听明白。
  • 使用@Configuration,您将获得相同的代理对象。使用@Component,您在所谓的精简模式下运行,您将获得一个代理,但当多次调用simpleBean() 时,您将获得一个新的代理实例。
  • @M.Deinum 这将是一个全新的副本,但不是代理,我认为。
  • 它将被代理,应用相同的语义,但对方法的调用不会通过配置代理。

标签: spring spring-aop


【解决方案1】:

让我给你另一个视角。

假设另一个 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());
    }        
 }

现在的问题是,从不同的方法simpleBeanConsumeranotherBeanConsumersimpleBean() 的两次调用怎么可能返回相同的简单bean 实例(因为它显然是一个单例)?

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

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

例如,如果您使用使用继承的 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),并且该类在运行时并不真正存在,那么spring如何加载使用此配置的配置的字节码并且不会在NoClassDefFoundException之类的东西上失败?

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

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

【讨论】:

  • Spring 确实使用 ASM(嵌入在 spring-core 依赖项中)来加载和处理 @Configuration 类。这也是@Configuration@Component类中处理@Bean方法的主要区别。
  • @M.Deinum 如果可能的话,您能否提供一些文档供我阅读?使用 @Configuration@Component 注释类时创建 Bean。
  • 您可以阅读 Spring 参考指南,该指南从高层次的角度解释了这一点。对于技术,我必须将您推荐给实际的源代码。
猜你喜欢
  • 1970-01-01
  • 2019-03-30
  • 1970-01-01
  • 2014-11-05
  • 2019-10-08
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 1970-01-01
相关资源
最近更新 更多