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 声明 @Bean
s 交易,或者是否有任何替代方案考虑以上限制条件 ?
我认为您可能不能以这种方式使用@Transactional。 spring 的内置后处理器之一应该扫描所有具有该注释的 classes(bean),并相应地创建方面。
关于备选方案:我会为我必须使用的每个第 3 方 class 编写一个适配器 class。然后让这些 Adapter classes 成为 Spring Beans。
您不能以这种方式使用 @Transactional
,但您可以使用 Spring 以编程方式配置方面。
Spring 以编程方式定义方面的文档:
- http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aspectj-programmatic
- 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;
}
我想从我的 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 声明 @Bean
s 交易,或者是否有任何替代方案考虑以上限制条件 ?
我认为您可能不能以这种方式使用@Transactional。 spring 的内置后处理器之一应该扫描所有具有该注释的 classes(bean),并相应地创建方面。
关于备选方案:我会为我必须使用的每个第 3 方 class 编写一个适配器 class。然后让这些 Adapter classes 成为 Spring Beans。
您不能以这种方式使用 @Transactional
,但您可以使用 Spring 以编程方式配置方面。
Spring 以编程方式定义方面的文档:
- http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aspectj-programmatic
- 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;
}