spring事务失效场景
首先spring的事务是使用AOP来实现的,而AOP的底层是代理(JDK代理或者CGLIB代理),所以事务失效就想什么时候不能进行代理
该描述的是使用注解@Transactional的方式来配置事务
-
配置的方法非public修饰
由于事务是使用的代理,而代理对于非public的方法不生效(private 不能被子类继承)
-
配置的所在类非spring容器管理的bean
-
注解修饰的方法被所在类使用this或默认调用
Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效
可以使用
xml<aop:config expose-proxy="true"></aop:config>
来暴露代理类
然后在代码中使用((MyServiceBiz)(AopContext.currentProxy())).test()来调用对应的方法
-
默认情况下,业务抛出异常为非RuntimeException异常
由于默认情况下只对于RuntimeException异常回滚
-
业务代码使用try...catch捕获异常,然后直接消化了,并未抛出异常
-
注解中设置了错误的传播方式
这里除了使用this调用外别的都比较好理解,只有this大家其实理解不了。举个例子
java
public class Base {
public void test(){
System.out.println("Base#this执行");
}
public void method(){
System.out.println("Base#method执行");
this.test();
}
}
public class Child extends Base{
public void test(){
System.out.println("Child#test执行");
}
public void invoke(){
super.method();
}
public static void main(String[] args) {
Child child = new Child();
child.invoke();
}
}
// 执行结果为
Base#method执行
Child#test执行
就算是使用cglib来继承重写父类方法,在执行this.test()时也该执行的是Child的方法,而不该是Base的方法,除非在执行method()的对象时就已经是父类对象了,就像这样
java
public class Child2 extends Base{
public void test(){
System.out.println("Child#test执行");
}
public void invoke(Base base){
base.method();
}
public static void main(String[] args) {
Child2 child = new Child2();
Base base = new Base();
child.invoke(base);
}
}
// 执行结果为
Base#method执行
Base#this执行
这里来看一下spring是怎么实现的。看一下DynamicAdvisedInterceptor
类,去掉与此次无关的代码
java
// proxy是代理类
// method是被代理类的方法
//
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
// 被代理类对象
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
// 没有代理的情况下,是直接执行方法的
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 有代理,创建CglibMethodInvocation执行代理链
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
}
在CglibMethodInvocation.proceed中看一下执行被代理方法是怎么执行的
java
protected Object invokeJoinpoint() throws Throwable {
if (this.publicMethod) {
// 发现传入的其实是 被代理类对象,也就是说我们真正执行的是被代理类对象,aop的增强逻辑是在执行链的执行的,采用的是类似组合的方式来实现的
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
return super.invokeJoinpoint();
}
}
本文由mdnice多平台发布