AOP全名Aspect-Oriented Programming,我們先來看看XXX-Oriented的意義,通常翻譯「XXX導向」,也就是以XXX為中心,例如中文中「客戶導向」就是以客戶為中心,而「物件導向」(OOP:Object-Oriented Programming)就是以物件為中心的程式設計。
自然的,Aspect-Oriented Programming,就是「Aspect導向程式設計」,也就是以Aspect為中心的程式設計,但什麼是Aspect?中文直譯通常是「方面」,但這個名詞容易使人混淆。
牛津字典中的英英解釋對Aspect是:particular part or feature of sth being considerd.
所以Aspect在英文中不只有「方面」的意思,還有部份(part)的意思。中文中稱「就這個方面來說」,通常指的是「就這個角度來說」或「就這個方向來說」,這個解釋不適用於AOP中的Aspect。如果英文中說from this aspect of sth,除了可以翻譯為上面兩句的意義之外,還可以翻作「就這個部份來說」。
以我們的前一個主題中的記錄(log)動作插入至HelloSpeaker物件的hello()中為例,我們說「就記錄這個部份」是不屬於HelloSpeaker職責的,它被硬生生切入HelloSpeaker中,英文中我們可以說:The logging aspect of the "hello" method doesn't belong to the job of HelloSpeaker.
所以以整個方法的執行流程來說,如果執行流程是縱向的,則記錄這個動作硬生生的「橫切」入其中,這個橫切入的部份我們就稱之為Aspect,它是橫切關注點(crosscutting concern,一個concern可以像是權限檢查、事務等等)的模組化,將那些散落在物件中各處的程式碼聚集起來。
所以Aspect要用中文表達的話,適切一些的名詞該是「橫切面」或「切面」。AOP關注於Aspect,將這些Aspect視作中心進行設計,使其中從職責被混淆的物件中分離出來,除了使原物件的職責更清楚之外,被分離出來的Aspect也可以設計的通用化,可運用於不同的場合。
來看事務管理這個Aspect如何在動態代理下被抽取出來,下面是一個簡單的概念,基本上是在Handler中先啟動事務,執行存儲層動作,方法執行成功則提交(commit),失敗則回滾(rollback):
代碼:
public class TransactionHandler implements InvocationHandler {
private Object delegate;
public Object bind(Object obj) {
this.delegate = obj;
return Proxy.new ProxyInstance(.., .., ..);
}
public Object invoke(Object proxy, Method method, Object[] args) {
Object result = null;
.....
YourTransaction transaction = null;
try {
transaction = yourMethodForGettingTransaction();
result = method.invoke(delegate, args);
transaction.commit();
}
catch(YourException e) {
if(transaction != null) {
try {
transaction.rollback();
}
catch(Exception e) {}
}
}
....
return result;
}
}
在AOP中,有好幾個關鍵的概念,然而其中更主要的是:PointCut、Advice與Advisor。這些術語並不是很直觀,我們配合我們上一個主題的HelloSpeaker來說明會比較容易理解。
PointCut是JointPoint的集合,JointPoint是指Aspect加入的階段點,例如某個方法被調用,某個成員被存取(Spring不支援),或是某個例外被丟出,以我們前一個主題的HelloSpeaker為例,hello()方法就是一個JointPoint。PointCut為JointPoint的集合,意味著多個方法或例外丟出可以使用同一個處理建議(Advice)。
Advice是在JointPoint上所要調用的處理建議(在JointPoint上所採取的動作,許多AOP框架通常以interceptor來實作Advice,之後會介紹),例如記錄、事務處理、權限檢查等。
Advice類型(advice type)有Around、Before、Throws、After returning。這些Advice類型會分別在以下的時機被調用:在JointPoint前後、JointPoint前、JointPoint丟出例外時、JointPoint執行完畢後。
就我們上一個例子來說,我們在hello()這個JointPoint上會調用Around Advice,即我們的LogHandler。事務管理調用的Advice類型也是屬於Around Advice,即上面的TransactionHandler,調用的JointPoint可能是某個DAO物件的saveXXX()方法。
Advisor將PointCut與Advice組合在一起,我們前一個主題中的HelloSpeaker例子很簡單,故而沒有使用到Advisor,之後會介紹到Advisor。
補充:動態代理是比較常見的AOP實作策略,在《Expert One-on-One J2EE Development WIthout EJB》 Rod Johnson、Juergen Hoeller中的第八章中有提到,AOP的實作有五個主要的策略:
- Dynamic Proxies
- Dynamic Byte Code Generation
- Java Code Generation
- Use of a Custom Class Loader
- Language Extensions
在該章中也作了不少對AOP的探討,以及現行幾個主流的AOP實作,像是Spring中的AOP、JBoss中的AOP與AspectJ等的比較,有興趣的可以去看看。