1. 程式人生 > >動態代理模式封裝事務詳解

動態代理模式封裝事務詳解

代理,大家都知道是什麼意思。百科上面的解釋:以他人的名義,在授權範圍內進行對被代理人直接發生法律效力的法律行為。

說白了就是A想交女朋友,但是自己不敢去表白,然後叫B去幫他送花,而B幫助A送了花,B就是代理。

而代理又分為靜態代理和動態代理,那麼什麼是靜態代理呢?

仍然是上面的例子,A想交女朋友,然後就就跟B說:B啊,咱倆是哥們兒,我喜歡那麼女生,你要幫我送一束花,幫我送洋娃娃,幫我送巧克力……然後A想送的時候,就叫B去送相應的東西。

動態代理是:A告訴B,你去幫我送花,然後B就去送花;A又說你去幫我送洋娃娃,B就又去送洋娃娃……

那麼靜態代理和動態代理有什麼區別呢?靜態代理提前都知道要幫A做那些事兒,有心理準備,行動起來比較快。但是由於A非常囉嗦,A需要表達強烈的愛意,跟B說了一大頓,B都需要記下A要幹啥,到時候去幫A做。

而動態代理是,B一直忙自己的事兒,當A有需求的時候,就幫A去送東西,由於A沒有提前告知B要送什麼,所以B要去現準備,所以需要花費一些時間去準備。

好了,說了這麼多廢話,就是為了用我自己的理解講解一下代理模式。下面進行正題——動態代理。

所謂動態代理,就是在執行時動態地建立一個代理類,實現一個或多個介面,並將方法的呼叫轉發到你所指定的類。


Proxy代理完全是java建立的,並且實現完整的subject介面。

InvocationHandler:Proxy上的任何方法呼叫都會被傳入此類,InvocationHandler控制對RealSubject具體行為類的訪問。

sun已經幫我們建立好了代理類Proxy,我們需要做的就是告訴Proxy:我們要做什麼。我們不能像以前一樣將程式碼寫入到Proxy中,因為它不是我們建立的。如果我們自己建立Proxy,那就是靜態代理了,這裡會有大量的重複程式碼,是我們不想看到的。由於InvocationHandler能夠響應代理的任何呼叫,我們可以把InvocationHandler想成是代理收到方法呼叫後,請求做際工作的物件。

下面通過動態代理封裝事務的例子進行講解:

事務在之前的專案中一直是加在Manager層,Manager層呼叫不同的dao方法,同時負責開啟事務,執行事務等。而由於每個Manger層的每個方法裡面除了要關心業務邏輯之外,都需要負責事務的開關。其實事務是單獨的邏輯,我們可以動態代理分離開來。

package com.xxjstgb.drp.util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

/**
 * 採用動態代理封裝事務
 * 
 * @author liuzhengquan
 * 
 */
public class TransactionHandler implements InvocationHandler {

	//要處理的物件,宣告為Object型別是為了通用性
	private Object targetObject;
	
	//動態生成方法被處理過後的物件 
	public Object newProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
		/**
		 * 引數1:類的載入器
		 * 引數2:確定繼承類的介面
		 */
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
				targetObject.getClass().getInterfaces(), 
				this);
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Connection conn = null;
		Object ret = null;
		try {
			//取得Connection
			conn = ConnectionManager.getConnection();
			//System.out.println(method.getName());
			/*
			 * 判斷Manager層呼叫的什麼方法,呼叫該方法時,自動開始事務
			 *注:此處DRP視訊中有錯誤
			 */
			if(method.getName().startsWith("addFlowCard") || 
			   method.getName().startsWith("delFlowCard") || 
			   method.getName().startsWith("modifyFlowCard") || 
			   method.getName().startsWith("findFlowCardList") || 
			   method.getName().startsWith("findClient") || 
			   method.getName().startsWith("findAimClient") 
			   ){
				System.out.println(method.getName());
				// 手動開啟事務
				ConnectionManager.beginTransaction(conn);
			}
			//呼叫目標物件的業務邏輯方法
			ret=method.invoke(targetObject, args);
			if(!conn.getAutoCommit()){
				//提交事務
				ConnectionManager.commitTransaction(conn);
			}
		}catch(Exception e){
			e.printStackTrace();
			//使用代理後,代理用InvocationTargetException包裝了異常
			if(e instanceof InvocationTargetException){
				InvocationTargetException ete=(InvocationTargetException)e;
				throw ete.getTargetException();
			}
			if(!conn.getAutoCommit()){
				//回滾事務
				ConnectionManager.rollbackTransaction(conn);
			}
			throw new ApplicationException("操作失敗!");
		}
		finally{
			//關閉事務,並刪除連線
			ConnectionManager.closeConnection();
		}
		return ret;
	}

}


這裡的Invoke方法,當代理的方法被呼叫的時候,代理就會把這個呼叫轉發給InvocationHandler,也就會呼叫它的invoke()方法。在Invoke方法裡面,能夠得到RealSubject具體行為方法,並且能夠定義新的行為,也就是這裡的事務操作。

*注:視訊中這裡有錯誤,method.getName()取得是呼叫的Manager方法,而不Servlet的方法。

對應Servlet的呼叫:

public class FlowCardServlet extends HttpServlet {
	//私有宣告具體行為類物件
	private FlowCardManager flowCardManager;
	@Override
	public void init() throws ServletException {
		flowCardManager=(FlowCardManager)getBeanFactory().getServiceObject(FlowCardManager.class);
		//採用動態代理包裝service
		TransactionHandler transactionHandler=new TransactionHandler();
		//對目標生成代理物件
		flowCardManager=(FlowCardManager)transactionHandler.newProxyInstance(flowCardManager);
	}
     }

這樣我們就建立了flowCardManager代理,當我們想呼叫Manager層的方法等,我們就可以通過在Servlet方法中通過使用flowCardManager代理物件,進行事務操作以及Manager層方法呼叫。

動態代理的應用非常廣範,比如WebService的應用,其實就是動態代理的一個應用。我們對WebService新增的引用,其實就是一個遠端代理,然後客戶端通過代理能夠進行遠端訪問。

生活中和實際應用中還有很多,都有代理的原型。希望大家多多與我交流,共同進步。