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(),經過此方法得到代理對象直接調用。
踩過的坑都是流過的淚。