問題來自:Spring事務的傳播行爲中REQUIRES_NEW真的有效嗎java
這個是Spring 對攔截的實現有關。Spring 攔截實現的方法是動態生成一個代理類。正常使用 @Autowired 註解注入的實際上就是這個代理類。git
一。 對於有接口實現的類代理,Spring 使用的是 Java 自帶的代理生成方式。這種方式對 target.method() 方式的調用是能夠攔截到的,對於類內調用 method() 方式則攔截不到。github
看如下代碼spring
public interface DynamicProxyInterface { void a(); void b(); }
public class DynamicProxy implements DynamicProxyInterface { @Override public void a() { System.out.println("this is a"); b(); } @Override public void b() { System.out.println("this is b"); } public static void main(String[] args) { DynamicProxy target = new DynamicProxy(); DynamicProxyInterface dynamicProxy = (DynamicProxyInterface) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke in proxy"); return method.invoke(target, args); } }); dynamicProxy.a(); } }
執行結果爲:ide
invoke in proxy this is a this is b
從這能夠看出你類內自行調用方法是不會被代理攔截到的,所以你使用的事務註解也就不會生效。測試
二。 對於單純的class,沒有接口,則 Spring 使用 cglib 進行代理,這裏 Spring實現了本身的 CallbackFilter,具體類能夠參見 Spring 源碼CglibAopProxy ,在目標類的invoke方法中,咱們能夠看到這塊代碼this
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; Object target = this.targetSource.getTarget();//獲取原始對象(未被代理) try { oldProxy = AopContext.setCurrentProxy(proxy); Object retVal = methodProxy.invoke(target, args); return processReturnType(proxy, target, method, retVal); } finally { AopContext.setCurrentProxy(oldProxy); this.targetSource.releaseTarget(target); } }
在第二行,咱們看到 Spring 獲取當前被代理的對象,直接進行invoke,類內方法也不會被cglib 代理到.net
咱們寫一個測試方法來試下,在上面main 方法裏最後加入測試代碼:代理
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(DynamicProxy.class); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("invoke in cglib"); return methodProxy.invoke(target, objects); } }); DynamicProxy cglibProxy = (DynamicProxy) enhancer.create(); cglibProxy.a(); }
執行結果(上面是Java proxy 輸出結果,與上面同樣)code
invoke in proxy this is a this is b invoke in cglib this is a this is b
Spring 針對這種狀況經過 threadlocal 的方式暴露了當前類的代理,能夠使用
AopContext.currentProxy();
方式獲得,使用獲取到的代理類再調用方法就能夠再次走事務的處理邏輯了。即
SpringTransactionMyBatisService service = (SpringTransactionMyBatisService)AopContext.currentProxy(); service.saveNew();
最後,若是你使用的 xml 配置,那麼須要在 aop 配置中,設置 expose-proxy 爲true
以上。