1. 程式人生 > 程式設計 >從Transactional與Async同時使用的錯誤到動態代理

從Transactional與Async同時使用的錯誤到動態代理

從Transactional與Async註解說起

當需要對某個方法開啟非同步執行緒,同時開啟事務時,使用Spring的新手大多會犯一個錯誤,將@Transactional@Async聯合使用。 這樣使用的一個直接後果是明明加了@Transactional註解為什麼事務沒有成功執行。

原因其實很簡單:

  1. Spring 實現這兩個註解的方式都是通過AOP
  2. 在實現時,Async註解強制覆蓋AOP的order為最小值(它認為Async應該是執行的AOP鏈中的第一個advisor -- jira.spring.io/browse/SPR-…
  3. 但是在實現Transactional註解時,卻沒有覆蓋order,這意味著它仍然為預設的Integer.MAX_VALUE,order可配置。所以非同步切面會先於事務切面執行。
  4. 假設@Transactional能先於Async切面執行,但由於spring事務管理依賴的是ThreadLocal,所以在開啟的非同步執行緒裡面感知不到事務,說細點就是在Spring開啟事務之後,會設定一個連線到當前執行緒,但這個時候又開啟了一個新執行緒,執行實際的SQL程式碼時,通過ThreadLocal獲取不到連線就會開啟新連線,也不會設定autoCommit,所以這個函式整體將沒有事務。

動態代理

動態代理就是通過反射機制或者位元組碼操縱等技術動態地獲取要被代理物件的型別,從而獲取相關特性進行代理。AOP就是動態代理的一種具體形式。

因為在開發過程中就遇到過,所以拿上面的例子做個開篇,在當時為瞭解決這個錯誤,理解其中原理,從而去深入探究了一番。

Spring實現的動態代理有兩種方式,JDK與Cglib。

這裡不去探究深層次原始碼,而是讓大家能在不同的開發場景中靈活使用動態代理這門重要的技術。

  • 這裡有一點需要注意:
    1. JDK代理的實現前提是被代理的類必須實現介面,因為我們代理的時候,正是根據繼承自介面然後構造的代理類。而Cglib則沒這個要求。
    2. cglib是針對類來實現代理的,其原理是對指定的要被代理類生成一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對final修飾的類進行代理。

假設有一個介面 TestP 與其實現類 TestPImpl

JDK動態代理實現

/**
  *JDK代理的實現前提是被代理的類,必須實現了介面,因為我們代理的時候,
  *正是根據繼承自介面然後構造的代理類。
*/
public class JdkProxy implements InvocationHandler{ private Object target; /** * 繫結需要被代理的物件 * 返回代理類 */ public Object bind(Object target) { this.target = target; //取得代理物件 return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { Object result=null; System.out.println("代理開始"); //你的方法 result=method.invoke(target,args); System.out.println("代理結束"); return result; } } public class TestJdkProxy { public static void main(String[] args) { JdkProxy jp = new JdkProxy(); TestP tp = (TestP)jp .bind(new TestPImpl()); tp.someMethod(); } } 複製程式碼

Cglib動態代理實現

public class CglibProxy implements MethodInterceptor {
 
	private Object target;
 
	public Object getInstance(Object target) {
		this.target = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.target.getClass());
		// 設定回撥方法  
		enhancer.setCallback(this);
		// 建立代理物件  
		return enhancer.create();
	}
 
	@Override
	// 回撥方法  
	public Object intercept(Object obj,Object[] args,MethodProxy proxy) throws Throwable {
		System.out.println("代理開始");
        //你的方法
		proxy.invokeSuper(obj,args);
		System.out.println("代理開始");
		return null;
 
	}
 
}

public class TestCglibProxy {
 
	public static void main(String[] args) {
		
		CglibProxy cp= new CglibProxy();
		TestP  tp = (TestP)cp.getInstance(new TestPImpl()); 
		tp.someMethod();
	}
	
}
複製程式碼