java Spring @Transactional in an Aspect (AOP)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27711615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring @Transactional in an Aspect (AOP)
提问by levacjeep
I've created an Aspect which contains an @Transactional annotation. My advice is being invoked as expected, but the new entity AuditRecord is never saved to the database, it looks like my @Transactional annotation is not working.
我创建了一个包含 @Transactional 注释的方面。我的建议正在按预期调用,但新实体 AuditRecord 从未保存到数据库中,看起来我的 @Transactional 注释不起作用。
@Aspect
@Order(100)
public class ServiceAuditTrail {
private AppService appService;
private FooRecordRepository fooRecordRepository;
@AfterReturning("execution(* *.app.services.*.*(..))")
public void logAuditTrail(JoinPoint jp){
Object[] signatureArgs = jp.getArgs();
String methodName = jp.getSignature().getName();
List<String> args = new ArrayList<String>();
for(Object arg : signatureArgs){
args.add(arg.toString());
}
createRecord(methodName, args);
}
@Transactional
private void createRecord(String methodName, List<String> args){
AuditRecord auditRecord = new AuditRecord();
auditRecord.setDate(new Date());
auditRecord.setAction(methodName);
auditRecord.setDetails(StringUtils.join(args, ";"));
auditRecord.setUser(appService.getUser());
fooRecordRepository.addAuditRecord(auditRecord);
}
public void setAppService(AppService appService) {
this.appService = appService;
}
public void setFooRecordRepository(FooRecordRepository fooRecordRepository) {
this.fooRecordRepository= fooRecordRepository;
}
}
The bean context is as follows:
bean上下文如下:
<tx:annotation-driven transaction-manager="txManager.main" order="200"/>
<aop:aspectj-autoproxy />
<bean id="app.aspect.auditTrail" class="kernel.audit.ServiceAuditTrail">
<property name="appService" ref="app.service.generic" />
<property name="fooRecordRepository" ref="domain.repository.auditRecord" />
</bean>
My pointcut is intercepting only interfaces (service interfaces). The service methods may or may not be transactional. If the service method is transactional, I would like that transaction to be rolled back if the Advice fails for some reason.
我的切入点仅拦截接口(服务接口)。服务方法可能是也可能不是事务性的。如果服务方法是事务性的,我希望在 Advice 由于某种原因失败时回滚该事务。
My question: Why is the transactional annotation being ignored? This is my first time building an AOP service with Spring, I would also welcome any architectural or implementation improvements.
我的问题:为什么事务注释被忽略?这是我第一次使用 Spring 构建 AOP 服务,我也欢迎任何架构或实现方面的改进。
Thanks!
谢谢!
回答by Federico Peralta Schaffner
In Spring, @Transactional
works by creating a proxy of your class (either a Java or cglib proxy) and intercepting the annotated method. This means that @Transactional
doesn't work if you are calling the annotated method from another method of the same class.
在 Spring 中,@Transactional
通过创建类的代理(Java 或 cglib 代理)并拦截带注释的方法来工作。这意味着@Transactional
如果您从同一类的另一个方法调用带注释的方法,这将不起作用。
Just move the createRecord
method to a new class (don't forget to make it a Spring bean too) and it will work.
只需将该createRecord
方法移动到一个新类(不要忘记也将其设为 Spring bean),它就会起作用。
回答by Zeus
A very good question. If you have to rollback/commit the transactions you can directly configure the spring transactional as mentioned here.
一个很好的问题。如果您有回滚/提交提到你可以直接配置Spring事务的交易在这里。
Doing this method does not require you to add @Transactional
on each of the class/method manually.
执行此方法不需要您@Transactional
手动添加每个类/方法。
Spring configuration is below(copied from the reference link ). Instead of writing custom advisors/pointcuts just add configuration in spring-application context.xml file with your advisors/pointcuts.
Spring 配置如下(从参考链接复制)。无需编写自定义顾问程序/切入点,只需在 spring-application context.xml 文件中使用您的顾问程序/切入点添加配置即可。
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!-- don't forget the DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
ref:
参考:
- Spring transactional : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html