当前位置: 首页 > 知识库问答 >
问题:

如果Spring可以在@Configuration类中成功拦截类内函数调用,为什么它不在常规bean中支持它呢?

公孙慎之
2023-03-14

我最近注意到Spring在@Configuration类中成功地拦截了类内函数调用,但在常规bean中却没有。

像这样的电话

@Repository
public class CustomerDAO {  
    @Transactional(value=TxType.REQUIRED)
    public void saveCustomer() {
        // some DB stuff here...
        saveCustomer2();
    }
    @Transactional(value=TxType.REQUIRES_NEW)
    public void saveCustomer2() {
        // more DB stuff here
    }
}

但是,在下面的示例中,当transactionManager()调用createDataSource()时,它会被正确截获,并调用代理的createDataSource(),而不是unwrapped类的createDataSource(),这可以通过在调试器中查看“this”来证明。

@Configuration
public class PersistenceJPAConfig {
    @Bean
    public DriverManagerDataSource createDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        //dataSource.set ... DB stuff here
        return dataSource;
    }

   @Bean 
       public PlatformTransactionManager transactionManager(   ){
           DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(createDataSource());
           return transactionManager;
       }
}

所以我的问题是,为什么Spring能在第二个例子中正确截取类内函数调用,而在第一个例子中却不能。它是否使用了不同类型的动态代理?

编辑:从这里的答案和其他来源,我现在理解了以下内容:@Transactional是使用Spring AOP实现的,其中代理模式是通过包装/组合user类来实现的。AOP代理足够通用,因此可以将许多方面链接在一起,并且可以是CGLib代理或Java动态代理。

在@Configuration类中,Spring还使用CGLib创建了一个从user@Configuration类继承的增强类,并用在调用user/super函数之前执行一些额外工作的函数覆盖user@bean函数,例如检查这是否是函数的第一次调用。这个类是代理吗?这取决于定义。您可能会说它是一个代理,它使用了来自真实对象的继承,而不是使用组合来包装它。

总而言之,从这里给出的答案,我理解这是两个完全不同的机制。为什么做出这些设计选择是另一个悬而未决的问题。

共有1个答案

严景焕
2023-03-14

因为AOP代理和@configuration类具有不同的用途,并且以显著不同的方式实现(即使两者都涉及到使用代理)。基本上,AOP使用组合,而@Configuration使用继承。

它们的工作方式基本上是创建代理,在将调用委托给原始(代理的)对象之前/之后执行相关的建议逻辑。容器注册这个代理而不是被代理的对象本身,因此所有依赖关系都被设置到这个代理,从一个bean到另一个bean的所有调用都通过这个代理。但是,代理对象本身没有指向代理的指针(它不知道自己被代理了,只有代理有指向目标对象的指针)。因此在该对象中对其他方法的任何调用都不会通过代理。

(我在这里添加它只是为了与@configuration形成对比,因为您似乎对此部分有正确的理解。)

它实际上是创建一个代理,它是@configuration类的子类。这样,它就可以拦截@configuration类的每个(非finalprivate)方法的调用,即使在同一个对象内也是如此(因为这些方法实际上都被代理覆盖,而Java将所有方法都虚拟化了)。代理正是这样做的,将它识别为(语义上)对Spring bean引用的任何方法调用重定向到实际的bean实例,而不是调用超类方法。

 类似资料:
  • 问题内容: 在Spring 3.0中有一个注释。它允许直接在Java代码中定义Spring bean。在浏览Spring参考时,我发现了使用此批注的两种不同方法-带注释的内部类和没有此注释的内部类。 本节包含以下代码: 在这里我们可以看到一段非常相似的代码,但是现在就在这里: 参考的前一部分包含以下说明: Spring组件中的@Bean方法的处理方式与Spring @Configuration类中

  • 问题内容: 我想在Java-SE应用程序中使用拦截器,并且将weld作为CDI实现,并且在这里进行测试: 主班: 服务等级: 拦截器类: Aaa和输出: 我的问题 第一:为什么我在调用methodCallNumberTwo()时没有在methodCall()中调用拦截器? 第二:有办法改变吗? 我仅研究拦截器的行为,并且想了解。先感谢您! 问题答案: 不会调用拦截器,因为您是在对象的同一实例上调用

  • 问题内容: 即使在不同的浏览器中,此代码也始终有效: 但是,我找不到关于为什么它应该起作用的单一参考。我首先在John Resig的演示文稿中看到了这一点,但仅被提及。那里或任何地方都没有解释。 有人可以启发我吗? 问题答案: 该声明是魔术,使它的标识符在代码块*中的任何内容执行之前就被绑定了。 这与带有表达式的赋值不同,后者以正常的自上而下的顺序求值。 如果将示例更改为说: 它将停止工作。 函数

  • 我有一个常规类(Soap.java),我添加了一个函数来调用一个webservice,我在res/values/strings.xml中有URL和其他信息。 IntenService: 片段:

  • 我正在学习Spring Core认证,我对这个基于学习材料的问题的答案有一些疑问。 为什么不允许使用@Configuration注释最终类 为了证实这一论断,我的理由如下: 考虑下面的配置类: 乍一看,这种情况可能看起来很奇怪,因为第一个方法(帐户存储库())将JdbcAcCountRepository对象实例化为一个bean,该对象具有id=AcCountRepository,遵循Spring默

  • 问题内容: 我有带有不同(HTML和JSON)结果类型的Struts2操作。他们使用通用拦截器。 如果需要拦截请求,如何根据给定的操作结果类型返回结果? 例如,我转发到JSP页面。如果操作是JSON类型,我想转发JSON错误。 问题答案: 我有带有不同(HTML和JSON)结果类型的Struts2操作。他们使用通用拦截器。如果需要拦截请求,如何根据给定的动作结果类型返回结果? 例如,我的Actio