spring的注解事物没有生效,异常数据没有回滚。java
同一个类中有多个方法,A方法没有开启事物,B方法经过注解开启事物,B方法的事物注解没有生效。代码以下:spring
package com.test.transcation; import org.springframework.transaction.annotation.Transactional; /** * Created by shaobo on 2018/4/9. */ public class Insert { public void a(){ this.b(); } @Transactional public void b(){ /** * 一通数据库操做 */ throw new RuntimeException(); } }
执行方法a(),方法b()中的数据成功更新到了数据库中,预期结果为数据回滚。数据库
咱们知道spring的事物是经过cglib来生成动态代理的。先来看JDK的动态代理。ide
package com.test.proxy; /** * 接口 */ public interface UserInterface { void update(); void complex(); }
package com.test.proxy; /** * 实现 */ public class UserService implements UserInterface { @Override public void update() { System.out.println("userDao.update()"); } @Override public void complex(){ System.out.println("begin complex()"); this.update(); System.out.println("end complex()"); } }
package com.test.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * InvocationHandler */ public class JDKProxy implements InvocationHandler { private Object target; public void bind(UserInterface userInterface){ target = userInterface; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before Method Invoke " + method.getName()); Object object = method.invoke(target,args); System.out.println("After Method Invoke " + method.getName()); return object; } }
package com.test.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { UserService userService = new UserService(); JDKProxy jdkProxy = new JDKProxy(); jdkProxy.bind(userService); UserInterface userInterface = (UserInterface)Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),jdkProxy); userInterface.complex(); } }
执行结果:咱们经过debug方式执行关键一下被代理对象this
咱们能够看到this对象为实际对象,因此update方法并无被拦截。spa
接下来咱们看一下cglib,debug
package com.test.cglib; /** * 代理对象 */ public class UserDao { public void update() { System.out.println("userDao.update()"); } public void complex() { System.out.println("begin complex()"); this.update(); System.out.println("end complex()"); } }
package com.test.cglib; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 代理类 */ public class DaoProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("Before Method Invoke " + method.getName()); methodProxy.invokeSuper(o, objects); System.out.println("After Method Invoke " + method.getName()); return o; } }
package com.test.cglib; import net.sf.cglib.proxy.Enhancer; public class Test { public static void main(String[] args) { DaoProxy daoProxy = new DaoProxy(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserDao.class); enhancer.setCallback(daoProxy); UserDao dao = (UserDao)enhancer.create(); dao.complex(); } }
执行结果以下设计
咱们能够发现cglib中的this指向代理对象,因此也会执行拦截方法。3d
spring aop的模型大体是这样的:代理
这样会致使methodB()并不能被通知到。我想若是以下图这样的话就不会出现这种问题,但spring这样这样设计确定有其理由,须要后续继续研究。
知道了缘由就好解决了,方法有以下两种。
一、不要使用spring 中嵌套aop,将这种嵌套放在两个类中(推荐)。
二、((UserInterface)AopContext.currentProxy()).update(),经过此方法得到代理对象直接调用。
踩过的坑都是流过的泪。