摘錄地址:http://www.javaworld.com.tw/jute/post/print?bid=5&id=81236
1.
從代理機(jī)制初探
AOP? Copy to clipboard
Posted by: caterpillar
Posted on: 2004-11-24 11:10
?
?
AOP
、
OOP
我覺得
AOP
觀念其實不難,只是很容易被中文混淆,
Aspect
在牛津字典中是這么解釋的:
particular part or feature of sth being considered.
?
Aspect
中文直譯通常作「方面」,方面在中文通常作「角度」解譯,我們常說「就這個方面來看」,其意是「就這個角度來看」,但在上面的英文解釋中,
aspect
有
part
的意義,中文來說是「就這個部份來看」,「我」覺得
Aspect
在中文上應(yīng)是取這個意思。
?
例如一個動作在執(zhí)行過程中的某個「部份」是
AOP
所關(guān)注的,這個部份切入了執(zhí)行過程,但本身不屬于執(zhí)行的最終目的,例如事務(wù)管理,執(zhí)行的目的可能是儲存數(shù)據(jù),但事務(wù)管理切入了這個過程,
AOP
關(guān)注于這個「切入面」,希望將其設(shè)計為通用,不介入對象設(shè)計的一個通用組件。
?
AOP
「我」比較接受的一個中文翻譯是「切面導(dǎo)向」,這可以免除「方面」兩字較具「表面」的意義,
Aspect
是一個「面」沒錯,但是具有橫切入過程的感覺。。。。。
?
總之,先看看下面的文章,我想應(yīng)該比較了解吧。。。。。
?
==================================
?
AOP
的全名是
Aspect-oriented Programming
,
Aspect
是程序設(shè)計時一個新的中心,
AOP
并不取代
OOP
,兩者各有各的角色,將職責(zé)各自分配自
Object
與
Aspect
,會使得程序中各個組件的角色更為清楚。
?
這邊先不直接探討
AOP
,我們先從一個簡單常見的例子來看看一個議題,這個例子是記錄(
log
)動作,程序中很常需要為某些動作或事件作下記錄,以便在事后檢視或是作為除錯時的信息,一個最簡單的例子如下:
import java.util.logging.*;
???????????????????????????????????????????????????????????????????????????????
public class HelloSpeaker {
??? private Logger logger = Logger.getLogger(this.getClass().getName());
???????????????????????????????????????????????????????????????????????????????
??
?public void hello(String name) {
??????? logger.log(Level.INFO, "hello method starts....");
???????????????????????????????????????????????????????????????????????????????
??????? System.out.println("Hello, " + name);
???????????????????????????????????????????????????????????????????????????????
??????? logger.log(Level.INFO, "hello method ends....");
??? }
}
?
?
HelloSpeaker
在執(zhí)行
hello()
方法時,我們希望能記錄該方法已經(jīng)執(zhí)行及結(jié)束,最簡單的作法就是如上在執(zhí)行的前后加上記錄動作,然而
Logger
介入了
HelloSpeaker
中,記錄這個動作并不屬于
HelloSpeaker
,這使得
HelloSpeaker
的職責(zé)加重。
?
想想如果程序中這種記錄的動作到處都有需求,上面這種寫法勢必造成我們必須復(fù)制記錄動作的程序代碼,使得維護(hù)記錄動作的困難度加大。如果不只有記錄動作,有一些非對象本身職責(zé)的相關(guān)動作也混入了對象之中(例如權(quán)限檢查、事務(wù)管理等等),會使得對象的負(fù)擔(dān)更形加重,甚至混淆了對象的職責(zé),對象本身的職責(zé)所占的程序代碼,或許遠(yuǎn)小于這些與對象職責(zé)不相關(guān)動作的程序代碼。
?
怎么辦,用下面的方法或許好一些,我們先定義一個接口,然后實作該接口:
public interface IHello {
??? public void hello(String name);
}
?
public class HelloSpeaker implements IHello {
??? public void hello(String name) {
??????? System.out.println("Hello, " + name);
??? }
}
?
?
接下來是重點了,我們實作一個代理對象
HelloProxy
:
import java.util.logging.*;
???????????????????????????????????????????????????????????????????????????????
public class HelloProxy implements IHello {
??? private Logger logger = Logger.getLogger(this.getClass().getName());
??? private IHello helloObject;
???????????????????????????????????????????????????????????????????????????????
??? public HelloProxy(IHello helloObject) {
??????? this.helloObject = helloObject;
??? }
???????????????????????????????????????????????????????????????????????????????
?
?? public void hello(String name) {
??????? logger.log(Level.INFO, "hello method starts....");
???????????????????????????????????????????????????????????????????????????????
??????? helloObject.hello(name);
??????????????????????????????????????????????
?????????????????????????????????
??????? logger.log(Level.INFO, "hello method ends....");
??? }
}
?
執(zhí)行時可以如此:
IHello helloProxy = new HelloProxy(new HelloSpeaker());
helloProxy.hello("Justin");
?
?
代理對象
HelloProxy
將代理真正的
HelloSpeaker
來執(zhí)行
hello()
,并在其前后加上記錄的動作,這使得我們的
HelloSpeaker
在撰寫時不必介入記錄動作,
HelloSpeaker
可以專心于它的職責(zé)。
?
這是靜態(tài)代理的基本范例,然而如您所看到的,代理對象的一個接口只服務(wù)于一種類型的對象,而且如果要代理的方法很多,我們勢必要為每個方法進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時就必定無法勝任。
?
Java
在
JDK 1.3
之后加入?yún)f(xié)助開發(fā)動態(tài)代理功能的類別,我們不必為特定對象與方法撰寫特定的代理,使用動態(tài)代理,可以使得一個
handler
服務(wù)于各個對象,首先,一個
handler
必須實現(xiàn)
java.lang.reflect.InvocationHandler
:
import java.util.logging.*;
import java.lang.reflect.*;
?????????????????????????????????????????????????????????????????????
?????????????????
public class LogHandler implements InvocationHandler {
??? private Logger logger = Logger.getLogger(this.getClass().getName());
??? private Object delegate;
????????????????????????????????????????????????????????????????????????????????
??????
??? public Object bind(Object delegate) {
??????? this.delegate = delegate;
??????? return Proxy.newProxyInstance(
?????????????????????????? delegate.getClass().getClassLoader(),
?????????????????????????? delegate.getClass().getInterfaces(),
????
??????????????????????this);
??? }
??????????????????????????????????????????????????????????????????????????????????????
??? public Object invoke(Object proxy, Method method, Object[] args)
??????????????? throws Throwable {
??????? Object result = null;
??????? try {
??????????? logger.log(Level.INFO, "method stats..." + method);
??????????? result = method.invoke(delegate, args);
??????????? logger.log(Level.INFO, "method ends..." + method);
??????? } catch (Exception e){
??????????? logger.log(Level.INFO, e.toString());
??????? }
??????? return result;
??? }
}
?
InvocationHandler
的
invoke()
方法會傳入被代理對象的方法名稱與執(zhí)行參數(shù)實際上要執(zhí)行的方法交由
method.invoke()
,并在其前后加上記錄動作,
method.invoke()
傳回的對象是實際方法執(zhí)行過后的回傳結(jié)果。
?
動態(tài)代理必須宣告接口,實作該接口,例如:
public interface IHello {
??? public void hello(String name);
}
?
public class HelloSpeaker implements IHello {
??? public void hello(String name) {
??????? System.out.println("Hello, " + name);
??? }
}
?
?
java.lang.reflect.Proxy
的
newProxyInstance()
依要代理的對象、接口與
handler
產(chǎn)生一個代理對象,我們可以使用下面的方法來執(zhí)行程序:
LogHandler logHandler? = new LogHandler();
IHello helloProxy = (IHello) logHandler.bind(new HelloSpeaker());
helloProxy.hello("Justin");
?
?
LogHandler
不在服務(wù)于特定對象與接口,而
HelloSpeaker
也不用插入任何有關(guān)于記錄的動作,它不用意識到記錄動作的存在。
?
講了半天,我們回到
AOP
的議題上,這個例子與
AOP
有何關(guān)系?
?
如上面的例子中示范的,
HelloSpeaker
本來必須插入記錄動作,這使得
HelloSpeaker
的職責(zé)加重,并混淆其原來的角色,為此,我們使用代理將記錄的動作提取出來,以厘清記錄動作與
HelloSpeaker
的職責(zé)與角色。
?
在這里,記錄這個動作是我們所關(guān)注的,
AOP
中的
Aspect
所指的就是像記錄這類的動作,我們將這些動作(或特定職責(zé))視為關(guān)注的中心,將其設(shè)計為通用、不介入特定對象、職責(zé)清楚的組件,這就是所謂的「
Aspect
導(dǎo)向」(比較好的一個中文翻法是「切面導(dǎo)向」,下一個主題再說明),就如同「對象導(dǎo)向」一樣,每個對象僅代表一個實際的個體,將對象視為關(guān)注的中心,將其設(shè)計為通用、職責(zé)清楚的組件。
?
這邊先以代理機(jī)制作為一個開端初步探討
AOP
,如您所看到的,
AOP
與
OOP
并不抵觸,兩者的合作使得程序更為清晰,
Object
與
Aspect
職責(zé)不互相混淆,而且都更具重用性,下一個主題我們再來深入
AOP
的一些細(xì)節(jié)。
?