Spring基于注解的AOP事务控制报错:Can’t call commit when autocommit=true
结论
没有考虑spring基于注解的AOP通知的执行顺序。
正文
当使用注解配置AOP时,在配置完四大通知(前置通知,后置通知,异常通知,最终通知)后,如下
package com.item.utils;
import com.sun.tracing.dtrace.Attributes;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
/**
* 和事务相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
@Aspect
@EnableAspectJAutoProxy
@Component("txManger")
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
@Pointcut("execution(* com.item.service.impl.AccountServiceImpl.*(..))")
private void pt1(){}
/**
* 开启事务
*/
@Before("pt1()")
public void beginTransaction(){
System.out.println("Before执行");
System.out.println();
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
@AfterReturning("pt1()")
public void commitTransaction(){
System.out.println("AfterReturning执行");
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
@AfterThrowing("pt1()")
public void rollbackTransaction(){
System.out.println("AfterThrowing执行");
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放连接
*/
@After("pt1()")
public void releaseTransaction(){
System.out.println("After执行");
try {
connectionUtils.getThreadConnection().close(); //还回连接池中
connectionUtils.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
然而在执行的时候却出异常。经过排查得知,没有考虑Spring基于注解AOP配置的执行顺序问题。
我们设想的是:前置通知 --> 切入点方法–> 后置通知 / 异常通知 --> 最终通知。
然而实际运行则是:前置通知 --> 切入点方法 --> 最终通知 --> 后置通知 /异常通知。
在实际运行中,connection在执行最终通知后,就已经结束了本次提交。然而此时后置通知/异常通知还未执行,所以又从连接池中获取了一个新的连接,此连接没有关闭自动提交,所以此时报错。
解决方法:
1、配置基于XML的AOP
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.item.service.impl.AccountServiceImpl.*(..))"/>
<aop:aspect ref="txManager">
<aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before>
<aop:after-returning method="commitTransaction" pointcut-ref="pt1"></aop:after-returning>
<aop:after-throwing method="rollbackTransaction" pointcut-ref="pt1"></aop:after-throwing>
<aop:after method="releaseTransaction" pointcut-ref="pt1"></aop:after>
</aop:aspect>
</aop:config>
2、配置基于环绕通知的AOP
@Around("pt1()")
public Object aroundNotif(ProceedingJoinPoint pjp){
Object returnVal = null;
Object[] args = pjp.getArgs();
try {
beginTransaction(); //前置通知
returnVal = pjp.proceed(args);
commitTransaction();//后置通知
return returnVal;
} catch (Throwable throwable) {
rollbackTransaction();//异常通知
throw new RuntimeException(throwable);
}finally {
releaseTransaction();//最终通知
}
}
配置完成后程序顺利执行!