Spring @Transactional on @Bean 声明而不是 class 实现

Spring @Transactional on @Bean declaration instead of class Implementation

我想从我的 Spring @Configuration class 配置 "transactional" bean,而不是用 [=15= 注释 class 实现本身].

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

原因是bean实现在另一个代码库中,它所属的模块不依赖Spring。阅读:我没有触及那个 bean 的源代码,只是实例化它。 class 是最终的,不能扩展它以向子 class 添加 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");
    }

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

@Configuration
@EnableTransactionManagement
public class ComplexComponentConfig {

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

    // ...
}

上面的示例确实不起作用,因为在运行时什么都没有得到 "transactional":即使抛出异常,实体 e1 仍然存在。

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

问题:是否可以从 @Configuration class 声明 @Beans 交易,或者是否有任何替代方案考虑以上限制条件 ?

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

关于备选方案:我会为我必须使用的每个第 3 方 class 编写一个适配器 class。然后让这些 Adapter classes 成为 Spring Beans。

您不能以这种方式使用 @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 的建议并编写适配器更容易,我不会感到惊讶。

找到了一些内置的东西,它是 @Mecon's and @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;
    }

// ...
}

您可以使用 spring 的 AOP 功能将事务拦截器添加到您的 bean。只需创建一个指定切入点的 Advisor bean 并添加一个 TranscationInterceptor.

@Bean
public ComplexComponent complexComponentImpl() {
    return new ComplexComponentImpl(repository());
}


@Bean
public Advisor advisorBean(TransactionManager txManager) {
  Class<?> targetClass = ComplexComponent .class;
  int propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED;

  return allMethodsTxAdvice(txManager, targetClass, propagationBehavior);
}

/**
  * Extracted method for reuse.
  */
private DefaultPointcutAdvisor allMethodsTxAdvice(TransactionManager txManager, Class<?> targetClass, int propagationBehavior) {
  AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  String pointcutExpression = MessageFormat.format("execution(* {0}.*(..)))", targetClass.getName());
  pointcut.setExpression(pointcutExpression);

  MatchAlwaysTransactionAttributeSource tas = new MatchAlwaysTransactionAttributeSource();
  TransactionAttribute transactionAttribute = new DefaultTransactionAttribute(propagationBehavior);
  tas.setTransactionAttribute(transactionAttribute);
  TransactionInterceptor transactionInterceptor = new TransactionInterceptor(txManager, tas);

  return new DefaultPointcutAdvisor(pointcut, transactionInterceptor);
}

PS:您不需要调用 afterPropertiesSet 只需 return FactoryBean 并且 spring 将处理所有生命周期回调。例如

@Bean
public FactoryBean<Object> complexComponent(TransactionManager tx) {
    TransactionProxyFactoryBean proxyFactory = new TransactionProxyFactoryBean();
    proxyFactory.setTransactionManager(tx);
    proxyFactory.setTarget(complexComponentImpl());

    Properties transactionAttributes = new Properties();
    transactionAttributes.put("*", "PROPAGATION_REQUIRED");
    proxyFactory.setTransactionAttributes(transactionAttributes);
    return proxyFactory;
  }