【问题标题】:Spring @Transactional on @Bean declaration instead of class ImplementationSpring @Transactional on @Bean 声明而不是类实现
【发布时间】:2015-07-03 09:38:34
【问题描述】:

我想从我的 Spring @Configuration 类中配置“事务性”bean,而不是使用 @Transactional 注释类实现本身。

有点像老派的方式,从 XML 文件配置事务建议,但不需要对我的类/方法名称的字符串引用来创建切入点。

原因是bean实现在另一个代码库中,它所属的模块不依赖于Spring。阅读:我没有接触那个 bean 的源代码,只是实例化它。该类是最终类,也不能对其进行扩展以向子类添加 Spring 注释。 为简单起见,假设所有方法都必须是事务性的。

bean 实现:

/** This class has no Spring dependency... */
// @Transactional <- which means I can't use this here
public final class ComplexComponentImpl implements ComplexComponent {

    private SomeRepository repo;

    public ComplexComponentImpl(SomeRepository repository) { this.repo = repository }

    public void saveEntities(SomeEntity e1, SomeEntity e2) {
        repo.save(e1);
        throw new IllegalStateException("Make the transaction fail");
    }

我想在我的配置类中做什么(在我的单元测试中不起作用):

@Configuration
@EnableTransactionManagement
public class ComplexComponentConfig {

    @Bean
    @Transactional // <- Make the bean transactional here
    public ComplexComponent complexComponent() {
        return new ComplexComponentImpl(repository());
    }

    // ...
}

确实,上面的示例不起作用,因为在运行时没有任何“事务性”:实体e1 被持久化,即使抛出异常。

请注意,我的事务管理设置与标有@Transactional 的实现类完美配合。

问题:是否可以从 @Configuration 类中声明 @Beans 事务性,或者考虑到上述约束是否有任何替代方法?

【问题讨论】:

    标签: java spring jpa transactions


    【解决方案1】:

    找到了一些内置的东西,它是 @Mecon@Erik Gillespie 的答案之和,样板有限。

    Spring 已经提供了一个TransactionProxyFactoryBean,它只是在任何对象上设置了一个事务代理。大部分设置可以重构为一些实用方法:

    @Configuration
    @EnableTransactionManagement
    public class ComplexComponentConfig {
    
        /** NOT A @Bean, this object will be wrapped with a transactional proxy */
        public ComplexComponent complexComponentImpl() {
            return new ComplexComponentImpl(repository());
        }
    
        @Bean
        public ComplexComponent complexComponent() {
            TransactionProxyFactoryBean proxy = new TransactionProxyFactoryBean();
    
            // Inject transaction manager here
            proxy.setTransactionManager(txManager());
    
            // Define wich object instance is to be proxied (your bean)
            proxy.setTarget(complexComponentImpl());
    
            // Programmatically setup transaction attributes
            Properties transactionAttributes = new Properties();
            transactionAttributes.put("*", "PROPAGATION_REQUIRED");
            proxy.setTransactionAttributes(transactionAttributes);
    
            // Finish FactoryBean setup
            proxy.afterPropertiesSet();
            return (ComplexComponent) proxy.getObject;
        }
    
    // ...
    }
    

    【讨论】:

    • 你试过了吗:@EnableTransactionManagement(proxyTargetClass = true)
    • 使用 Spring 4.3.1,此解决方案失败,java.lang.ClassCastException: com.sun.proxy.$Proxy460 cannot be cast to foo.bar.ComplexComponentImpl 来自 return 语句。还没有深入研究这个问题。
    • ...**从之前的评论更新**没有使用接口,所以需要proxy.setProxyTargetClass(true)
    【解决方案2】:

    我认为您可能不能以这种方式使用@Transactional。 spring 的内置 PostProcessor 之一应该扫描所有具有该注释的类(bean),并相应地创建 Aspects。

    关于替代方案:我将为我必须使用的每个 3rd 方类编写一个适配器类。然后让那些 Adapter 类成为 Spring Beans。

    【讨论】:

      【解决方案3】:

      您不能以这种方式使用@Transactional,但您可以使用 Spring 以编程方式配置方面。

      以编程方式定义方面的 Spring 文档:

      1. http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aspectj-programmatic
      2. http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop-api.html

      文档中的示例非常简单。定义事务方面可能会更复杂,如果您发现使用基于 XML 的代理的便利性或接受@Mecon 的建议并编写适配器更容易,我不会感到惊讶。

      【讨论】:

        猜你喜欢
        • 2018-04-04
        • 2013-05-05
        • 1970-01-01
        • 1970-01-01
        • 2015-02-23
        • 2019-07-03
        • 1970-01-01
        • 1970-01-01
        • 2011-01-05
        相关资源
        最近更新 更多