功能代碼的多余枝節(jié)
當我們書寫執(zhí)行一個功能的函數(shù)時,經(jīng)常需要在其中寫入與功能不是直接相關(guān)但很有必要的代碼,如日志記錄,信息發(fā)送,安全和事務(wù)支持等,以下代碼是一個用戶注冊類的代碼:

/** *//**
* 用於用戶注冊的服務(wù)類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:15:25
*/

public class RegisterService
{

/** *//**
* 注冊一個用戶
* @param name 用戶名
* @param pswd 用戶密碼
* @param email 用戶郵件地址
*/

public void register(String name,String pswd,String email)
{
Logger.log("將注冊一個新用戶"+name);
// 真正的,應(yīng)該由本函數(shù)擔負的處理
System.out.println("存儲用戶信息到數(shù)據(jù)庫");
MailSender.send(email, "歡迎"+name+"注冊為本系統(tǒng)的用戶");
}
}

Logger類代碼

/** *//**
* 模擬記錄器
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:17:56
*/

public class Logger
{

/** *//**
* 模擬記錄信息到文件中
* @param str
*/

public static void log(String str)
{
System.out.println(getCurrTime()+"INFO:"+str);
}

/** *//**
* 取得當前時間
* @return
*/

private static String getCurrTime()
{
Date date = new Date();
Format formatter = new SimpleDateFormat("HH時mm分ss秒");
return formatter.format(date);
}
}


MailSender類代碼

/** *//**
* 模擬郵件發(fā)送器
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:23:31
*/

public class MailSender
{

/** *//**
* 模擬發(fā)送郵件
* @param title
* @param msg
*/

public static void send(String email,String concept)
{
System.out.println("向"+email+"發(fā)送郵件 內(nèi)容為:"+concept+"的郵件");
}
}


枝節(jié)性代碼給功能性代碼帶來的麻煩
諸如日志記錄,信息發(fā)送,安全和事務(wù)支持等枝節(jié)代碼雖然是必要的,但它會帶來以下麻煩:
1.枝節(jié)性代碼游離在功能性代碼之外,它們不是函數(shù)的目的,這對OO是一種破壞。
2.枝節(jié)性代碼會造成功能性代碼對其它類的依賴,加深類之間的耦合度,而這是OO系統(tǒng)所竭力避免的。
3.枝節(jié)性代碼帶來的耦合度會造成功能性代碼移植困難,可重用性降低。
4.從法理上說,枝節(jié)性代碼應(yīng)該“監(jiān)視”著功能性代碼,然后采取行動;而不是由功能性代碼“通知”枝節(jié)性代碼采取行動。這好比吟游詩人應(yīng)該是主動記述騎士的功績而不是騎士主動要求詩人記錄自己的功績的。
如何兩種代碼分離開來
毫無疑問,枝節(jié)性代碼和功能性代碼(主干性代碼)需要分離開來才能降低耦合程度,符合現(xiàn)代OO系統(tǒng)的要求,而java提供的動態(tài)代理機制可以幫助我們實現(xiàn)這一點。
動態(tài)代理機制主要的類是java.lang.reflect.Proxy,它從一誕生就受到了重視,并在RMI,EJB和AOP中都得到廣泛的應(yīng)用,其重要程度唯有反射能與之相比。
Proxy代理模式
在講述動態(tài)代理之前我們可以回顧一下代理模式,它的定義是這樣的:代理可以提供對另一個對象的訪問,同時隱藏實際對象的具體事實。代理一般會實現(xiàn)它所表示的實際對象的接口。代理可以訪問實際對象,但是延遲實現(xiàn)實際對象的部分功能,實際對象實現(xiàn)系統(tǒng)的實際功能,代理對象對客戶隱藏了實際對象。客戶不知道它是與代理打交道還是與實際對象打交道。
如果我們使用代理模式,把枝節(jié)性代碼放入代理類中,這樣主干性代碼保持在真實的類中,這樣不就能有效降低耦合度嗎?這種通過在耦合緊密的類之間引入一個中間類是降低類之間的耦合度的常見做法。
具體來說就是把枝節(jié)性代碼放入代理類中,它們由代理類負責調(diào)用,而真實類只負責主干的核心業(yè)務(wù),它也由代理類調(diào)用,它并不知道枝節(jié)性代碼的存在和作用,因為這本不是它的任務(wù)。對外來說,代理類隱藏在接口之后,客戶并不清楚也不需要清楚具體的調(diào)用過程。通過這樣的處理,主干與枝節(jié)之間的交叉解開了,外界的調(diào)用也沒有復(fù)雜化,這就有效降低系統(tǒng)各部分間的耦合度。
下面讓我們先看看代碼
消除了枝節(jié)代碼的注冊類

/** *//**
* 用於用戶注冊的服務(wù)類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:15:25
*/

public class RegisterService implements IService
{

/** *//**
* 注冊一個用戶
* @param name 用戶名
* @param pswd 用戶密碼
* @param email 用戶郵件地址
*/

public void register(String name,String pswd,String email)
{
// 真正的,該由本函數(shù)擔負的處理
System.out.println("存儲用戶信息到數(shù)據(jù)庫");
}
}


注冊類的代理類,枝節(jié)性代碼都被轉(zhuǎn)移到了這里

/** *//**
* 注冊服務(wù)代理類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:45:10
*/

public class RegisterServiceProxy implements InvocationHandler
{
// 代理對象
Object obj;
// 構(gòu)造函數(shù),傳入代理對象

public RegisterServiceProxy(Object o)
{
obj = o;
}


/** *//**
* 調(diào)用被代理對象的將要被執(zhí)行的方法,我們可以在調(diào)用之前進行日誌記錄,之后執(zhí)行郵件發(fā)送
*/

public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
{
Object result = null;

try
{
// 進行日誌記錄
String name=(String)args[0];
Logger.log("將注冊一個新用戶"+name);

// 調(diào)用Object的方法
result = m.invoke(obj, args);
// 執(zhí)行郵件發(fā)送
String email=(String)args[2];
MailSender.send(email, "歡迎"+name+"注冊為本系統(tǒng)的用戶");

} catch (InvocationTargetException e)
{

} catch (Exception eBj)
{

} finally
{
// Do something after the method is called 
}
return result;
}
}


代理類RegisterServiceProxy的解釋
該代理類的內(nèi)部屬性為Object類,實際使用時通過該類的構(gòu)造函數(shù)RegisterServiceProxy(Object obj)對其賦值;此外,在該類還實現(xiàn)了invoke方法,該方法中的
method.invoke(obj,args);
其實就是調(diào)用被代理對象的將要被執(zhí)行的方法,這是通過反射實現(xiàn)的,方法參數(shù)obj是實際的被代理對象,args為執(zhí)行被代理對象相應(yīng)操作所需的參數(shù)。通過動態(tài)代理類,我們可以在調(diào)用之前或者之后執(zhí)行一些相關(guān)操作。
如何生成一個代理類的實例
代理類的實例需要特殊的方式生成,代碼如下:

public static IService genereteService()
{
return (IService)Proxy.newProxyInstance(
IService.class.getClassLoader(),

new Class[]
{IService.class},
new RegisterServiceProxy(new RegisterService()));
}
Proxy即為java中的動態(tài)代理類,其方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,其中l(wèi)oader是類加載器,interfaces是被代理的真實類的接口,h是具體的代理類實例。
所謂動態(tài)代理是這樣一種class:它是在運行時生成的類,在生成它時你必須提供一組接口給它,然后該類就宣稱它實現(xiàn)了這些接口。你當然可以把該類的實例當作這些接口中的任何一個實現(xiàn)類來用。當然啦,這個動態(tài)代理類其實就是一個代理,它不會做作實質(zhì)性的工作,而是在生成它的實例時你必須提供一個真實的類的實例,由它接管實際的工作。
工廠方法的作用
對于代理類生成的細節(jié),客戶(需要使用RegisterService的程序員)是沒有興趣也沒有必要知道的,我們可以讓它隱藏在一個工廠方法中,對外返回一個接口,這樣在調(diào)用時用戶就不知道他是與代理打交道還是與實際對象打交道了。使用RegisterService類時示例代碼如下:
IService service=RegisterServiceFactory.genereteService();
service.register("sitinspring","123456","junglesong@gmail.com");
執(zhí)行完的結(jié)果和前面的代碼的是一樣的。
動態(tài)代理在AOP中的應(yīng)用
Spring的AOP支持可以被用于從系統(tǒng)核心邏輯中分離交叉業(yè)務(wù)(cross-business)如日志,事務(wù)管理和安全等,使用AOP,你可以用各種功能層來覆蓋核心業(yè)務(wù)層,這些功能層可以靈活的應(yīng)用到你的系統(tǒng)中,甚至核心業(yè)務(wù)層都不知道它們的存在,這是一個強大的概念。
AOP(aspect-oriented programming)的核心就是動態(tài)代理,掌握它對于理解AOP尤為重要,猶如反射對理解IoC一樣。
代碼下載:
http://www.tkk7.com/Files/sitinspring/DynamicProxySample20080527235441.rar