關於在Spring 中方法內部調用自身方法事務 REQUIRE_NEW 不生效的解釋

問題來自: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

 

以上。

相關文章
相關標籤/搜索