摘錄地址:http://www.javaworld.com.tw/jute/post/print?bid=5&id=81236
1.
從代理機制初探
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
在中文上應是取這個意思。
?
例如一個動作在執行過程中的某個「部份」是
AOP
所關注的,這個部份切入了執行過程,但本身不屬于執行的最終目的,例如事務管理,執行的目的可能是儲存數據,但事務管理切入了這個過程,
AOP
關注于這個「切入面」,希望將其設計為通用,不介入對象設計的一個通用組件。
?
AOP
「我」比較接受的一個中文翻譯是「切面導向」,這可以免除「方面」兩字較具「表面」的意義,
Aspect
是一個「面」沒錯,但是具有橫切入過程的感覺。。。。。
?
總之,先看看下面的文章,我想應該比較了解吧。。。。。
?
==================================
?
AOP
的全名是
Aspect-oriented Programming
,
Aspect
是程序設計時一個新的中心,
AOP
并不取代
OOP
,兩者各有各的角色,將職責各自分配自
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
在執行
hello()
方法時,我們希望能記錄該方法已經執行及結束,最簡單的作法就是如上在執行的前后加上記錄動作,然而
Logger
介入了
HelloSpeaker
中,記錄這個動作并不屬于
HelloSpeaker
,這使得
HelloSpeaker
的職責加重。
?
想想如果程序中這種記錄的動作到處都有需求,上面這種寫法勢必造成我們必須復制記錄動作的程序代碼,使得維護記錄動作的困難度加大。如果不只有記錄動作,有一些非對象本身職責的相關動作也混入了對象之中(例如權限檢查、事務管理等等),會使得對象的負擔更形加重,甚至混淆了對象的職責,對象本身的職責所占的程序代碼,或許遠小于這些與對象職責不相關動作的程序代碼。
?
怎么辦,用下面的方法或許好一些,我們先定義一個接口,然后實作該接口:
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....");
??? }
}
?
執行時可以如此:
IHello helloProxy = new HelloProxy(new HelloSpeaker());
helloProxy.hello("Justin");
?
?
代理對象
HelloProxy
將代理真正的
HelloSpeaker
來執行
hello()
,并在其前后加上記錄的動作,這使得我們的
HelloSpeaker
在撰寫時不必介入記錄動作,
HelloSpeaker
可以專心于它的職責。
?
這是靜態代理的基本范例,然而如您所看到的,代理對象的一個接口只服務于一種類型的對象,而且如果要代理的方法很多,我們勢必要為每個方法進行代理,靜態代理在程序規模稍大時就必定無法勝任。
?
Java
在
JDK 1.3
之后加入協助開發動態代理功能的類別,我們不必為特定對象與方法撰寫特定的代理,使用動態代理,可以使得一個
handler
服務于各個對象,首先,一個
handler
必須實現
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()
方法會傳入被代理對象的方法名稱與執行參數實際上要執行的方法交由
method.invoke()
,并在其前后加上記錄動作,
method.invoke()
傳回的對象是實際方法執行過后的回傳結果。
?
動態代理必須宣告接口,實作該接口,例如:
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
產生一個代理對象,我們可以使用下面的方法來執行程序:
LogHandler logHandler? = new LogHandler();
IHello helloProxy = (IHello) logHandler.bind(new HelloSpeaker());
helloProxy.hello("Justin");
?
?
LogHandler
不在服務于特定對象與接口,而
HelloSpeaker
也不用插入任何有關于記錄的動作,它不用意識到記錄動作的存在。
?
講了半天,我們回到
AOP
的議題上,這個例子與
AOP
有何關系?
?
如上面的例子中示范的,
HelloSpeaker
本來必須插入記錄動作,這使得
HelloSpeaker
的職責加重,并混淆其原來的角色,為此,我們使用代理將記錄的動作提取出來,以厘清記錄動作與
HelloSpeaker
的職責與角色。
?
在這里,記錄這個動作是我們所關注的,
AOP
中的
Aspect
所指的就是像記錄這類的動作,我們將這些動作(或特定職責)視為關注的中心,將其設計為通用、不介入特定對象、職責清楚的組件,這就是所謂的「
Aspect
導向」(比較好的一個中文翻法是「切面導向」,下一個主題再說明),就如同「對象導向」一樣,每個對象僅代表一個實際的個體,將對象視為關注的中心,將其設計為通用、職責清楚的組件。
?
這邊先以代理機制作為一個開端初步探討
AOP
,如您所看到的,
AOP
與
OOP
并不抵觸,兩者的合作使得程序更為清晰,
Object
與
Aspect
職責不互相混淆,而且都更具重用性,下一個主題我們再來深入
AOP
的一些細節。
?