Spring在@Bean声明上使用@Transactional,而不是类Implementation



我想从Spring@Configuration类中配置"transactional"bean,而不是用@Transactional注释类实现本身。

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

原因是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类中声明@Bean的事务性,或者考虑到上述限制,是否有其他选择?

发现了一些内置的东西,它是@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;
}
// ...
}

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

关于备选方案:我会为我必须使用的每个第三方类编写一个Adapter类。然后使这些Adapter类成为SpringBeans。

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

用于编程定义方面的Spring文档:

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

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

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

@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,只需返回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;
}

最新更新