目录
如果知道了事务的处理流程,去理解事务传播属性导致的回滚就是分分钟的事儿了。
想要知道事务的拦截处理流程,只需要分析TransactionAspectSupport类的部分源码即可
// 事务拦截处理方法
protected Object invokeWithinTransaction(Method method, Class targetClass, final TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
final TransactionAttribute txAttr = this.getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = this.determineTransactionManager(txAttr);
final String joinpointIdentification = this.methodIdentification(method, targetClass);
// 以下代码只是部分代码,为了方便看只截取了能说明问题的部分
// 1.获取事务
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// 2.处理真正调用的方法
retVal = invocation.proceedWithInvocation();
} catch (Throwable var15) {
// 3.有异常回滚:
this.completeTransactionAfterThrowing(txInfo, var15);
throw var15;
} finally {
this.cleanupTransactionInfo(txInfo);
}
// 4.处理方法完成,提交事务
this.commitTransactionAfterReturning(txInfo);
return retVal;
}
从上面注释可以看到,任何一个被事务拦截的方法,都是先在真正调用该方法之前获取了事务,执行完该方法后再决定是事务回滚或提交。
我们还可以看到,调用处理真正要执行的方法是被try catch的,而catch块里才会有事务回滚的代码。所以,如果某个事务方法A内部有异常没有抛出来(被自己的try cathch块捕获了),而不能被这里的try catch块捕获到,那么这个方法A就不会被回滚。
不会回滚的例子:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void saveTx() {
try {
Log log = new Log();
log.setTitle("saveTx");
log.setBeginDate(new Date());
logService.save(log);
int i=0;
int j=2/i;
}catch (Exception e){
e.printStackTrace();
}
}
将会回滚的例子:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void saveTx() {
Log log = new Log();
log.setTitle("saveTx");
log.setBeginDate(new Date());
logService.save(log);
int i=0;
int j=2/i;
}
在我们写代码的时候,有时候会考虑某个方法是否应该被回滚,那么我们就得注意方法里的异常是否该被try catch是非常重要的。
还有一点,如果一个事务方法调用了一个非事务方法,那么非事务方法就要看作是事务方法的一部分,它们共用的一个数据库连接。
表格中只要说到加入父事务后,就为同一个事务,他们会共用一个Connection
传播属性 | 说明 |
required | 如果当前存在事务则加入该事务,如果没有则新建一个事务 |
supports | 如果当前存在事务则加入该事务,如果没有则以非事务的方式运行。 |
mandatory | 强制加入当前的事务。如果当前没有事务,就抛出异常。 |
requires_new | 新建事务。如果当前存在事务,把当前事务挂起。 |
not_supported | 不支持事务。如果当前存在事务,就把当前事务挂起,然后以非事务方式执行。 |
never | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
nested | 如果当前存在事务,则嵌套执行(相当于指定了回滚点SavePoint)。如果当前没有事务,则新建事务。 |
经历过的都知道,Exception异常如果不在rollback-for属性当中指定,即使出现了Exception异常也不会发生事务回滚。
这是因为spring事务处理时,只对RuntimeException和Error异常进行了处理,而Exception没有在其中。
我们看看TransactionAspectSupport类回滚方法里的代码:下面代码只看注释就行了
protected void completeTransactionAfterThrowing(TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
}
// 判断是否是要回滚的异常:rollbackOn方法代码在后面
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} catch (TransactionSystemException var7) {
this.logger.error("Application exception overridden by rollback exception", ex);
var7.initApplicationException(ex);
throw var7;
} catch (RuntimeException var8) {
this.logger.error("Application exception overridden by rollback exception", ex);
throw var8;
} catch (Error var9) {
this.logger.error("Application exception overridden by rollback error", ex);
throw var9;
}
// 不在指定异常范围内
} else {
try {
// 事务提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
} catch (TransactionSystemException var4) {
this.logger.error("Application exception overridden by commit exception", ex);
var4.initApplicationException(ex);
throw var4;
} catch (RuntimeException var5) {
this.logger.error("Application exception overridden by commit exception", ex);
throw var5;
} catch (Error var6) {
this.logger.error("Application exception overridden by commit error", ex);
throw var6;
}
}
}
}
异常判断方法:
// RuleBasedTransactionAttribute类的方法
public boolean rollbackOn(Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = 2147483647;
if (this.rollbackRules != null) {
Iterator var4 = this.rollbackRules.iterator();
// 遍历rollback规则
while(var4.hasNext()) {
RollbackRuleAttribute rule = (RollbackRuleAttribute)var4.next();
// 查找当前异常是否在规则里面
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
// 该异常没在rollback规则里,则再去判断是否是RuntimeException或Error
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
// 这里调用的方法参考
return super.rollbackOn(ex);
} else {
// rollback规则里有此异常就返true,即要回滚
return !(winner instanceof NoRollbackRuleAttribute);
}
}
代码片段3:
// 如果是RuntimeException或Error异常的子类就返回true
public boolean rollbackOn(Throwable ex) {
// 只指定了这两个异常,没有Exception异常
return ex instanceof RuntimeException || ex instanceof Error;
}
rollback-for属性只针对Error和RuntimeException
我们知道,spring的事务都是通过AOP动态代理实现的。如果想要任何一个方法实现事务代理,就必须通过事务代理类去调用,而内部方法调用与事务代理类一点我关系都木有,所以不会生效。下面我举个例子
要代理的接口类:
public interface Subject {
void methodOne();
void methodTwo();
}
要代理的真实类:
public class RealSubject implements Subject {
@Override
public void methodOne() {
System.out.println("one");
this.menthoTwo();
}
@Override
public void methodTwo() {
System.out.println("two");
}
}
代理类要调用的事务处理器(代理类就是通过它对方法实现的额外处理):
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(){
}
public MyInvocationHandler(Object obj){
this.obj = obj;
}
// 调用方法前的额外处理:记录方法调用开始时间
public void startRecordRequestTime(){
System.out.println("方法调用开始时间:"+System.currentTimeMillis());
}
// 调用方法前的额外处理:模拟处理事务的传播属性
public void processTransaction(){
System.out.println("事务处理..........");
}
// 调用方法后的额外处理:记录方法调用结束时间
public void endRecordRequestTime(){
System.out.println("方法调用结束时间:"+System.currentTimeMillis());
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.startRecordRequestTime();
// 调用处理事务传播属性方法
this.processTransaction();
// 通过反射执行真实的方法
method.invoke(obj,args);
this.endRecordRequestTime();
return null;
}
}
使用代理访问:
public class Test {
public static void main(String[] args) {
Subject sub = new RealSubject();
MyInvocationHandler handler = new MyInvocationHandler(sub);
// 利用反射动态生成的代理类
Subject proxy=(Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),new Class[]{Subject.class},handler);
// 通过代理类访问方法
proxy.methodOne();
}
}
//运行结果为:
方法调用开始时间:1531564567403
事务处理..........
one
two
方法调用结束时间:1531564567403
.
好了,从执行了proxy.methodOne();
这句代码后的结果能看到,当代理类调用了methodOne()
后,methodTwo()
是在内部调用的,与代理类一点儿关系也没有,所以methodOne
调用内部的methodTwo
时,是不会走事务处理代码的,所以事务传播属性也就会失效。
上面动态生成的代理类大概如下,有兴趣的可以了解下,重点是下面的重写方法调用的MyInvocationHandler
类的invoke
方法:
public class $Proxy0 extends Proxy implements Subject {
private static Method m0;
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
// 方法1
m3 = Class.forName("***.RealSubject").getMethod("methodOne",
new Class[0]);
// 方法2
m4 = Class.forName("***.RealSubject").getMethod("methodTwo",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
// 重写Subject接口方法一
@Override
public final void methodOne() {
try {
// 调用事务增强处理方法
super.h.invoke(this,m3,null);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
// 重写Subject接口方法二
@Override
public final void methodTwo() {
try {
// 调用事务增强处理方法
super.h.invoke(this,m4,null);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}