AOP及其在Spring中的應用(一) .

li_xiao_ming發表於2012-12-18

OOP思想對現代程式設計產生了深遠的影響,但在某些方面,OOP也有其不足之處。比如在logging(日誌)、transaction(事務)等方面,應用OOP將這些內容封裝為物件的行為則會產生大量的程式碼重複,雖然通過一些設計模式可以減少這種重複,但我們還有更好的解決辦法,那就是AOP(Aspect Oriented Programming)。AOP是最近興起的一種程式設計思想,它是OOP思想的補充,而不是其對立面。
AOP,從字面的理解來看就是面向切面的程式設計,用一個比較通俗的例子來說,比如在訪問多個物件前需要進行許可權檢查,那麼如果按照物件導向的思路來說,許可權檢查勢必會成為這多個物件的行為。如果每個物件都需要去實現這些行為,勢必會造成大量重複程式碼的產生,寫程式也會變得枯燥無味。但我們可以將許可權檢檢視作是一個切面,所有對這些物件的訪問都要經過這個切面。要了解AOP,就必須先了解幾個基本的且非常重要的概念。


Aspect(切面):物件操作過程中的截面。如許可權檢查、日誌、事務處理等。
Join Point(連線點):程式執行中的某個階段點。如某個方法呼叫,異常丟擲等。
Advice(處理邏輯):某個連線點所採用的處理邏輯。
PointCut(切點):一系列連線點的集合,它指明Advice在什麼時候被觸發。

示例

還是用例子來說明一切,比如現在有一個DomainObjDAO介面以及其實現類DomainObjDAOImpl
DomainObjDAO.java:
public interface DomainObjDAO {
public void save();
}
DomainObjDAOImpl:
public class DomainObjDAOImpl implements DomainObjDAO {
private Logger logger = Logger.getLogger(this.getClass().getName());
public void save() {
System.out.println("saving domain object......");
}
現在需要在save方法中新增對該業務物件的鎖,比如在save前後加鎖和解鎖。拿到這個需求,在不影響外部呼叫邏輯以及不對現有程式碼改動的前提下,Proxy模式(GOF)是個不錯的選擇,新增一個Proxy類同樣實現DomainObjDAO介面,在其實現方法中代理DomainObjDAOImpl類的save方法,並在save的前後呼叫lock以及unlock方法。這種方法使得我們不必改動外部呼叫邏輯以及現有程式碼,但是如果有多個DomainObjImpl的情況下,該方法的弊端便暴露無遺,我們必須實現與DomainObjImpl個數相同的Proxy類來實現該功能,這對我們來說將是非常恐怖且不可接受的。
這個例子再次印證我們開始所描述的,針對這類問題,OOP顯得有些力不從心,而AOP卻能很好的解決它,JDK1.3後所提供的動態代理的特性為我們利用AOP的思想解決這個問題提供了很好的思路,下面我們來看它如何實現。

 

動態代理實現AOP

public class LockHandler implements InvocationHandler {
private Logger logger = Logger.getLogger(this.getClass().getName());
private Object originalObject;

public Object bind(Object obj) {
logger.info("coming here...");
this.originalObject = obj;
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),this);}

public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
Object result=null;
if(arg1.getName().startsWith("save")){
lock();
result=arg1.invoke(this.originalObject,arg2);
unlock();
}
return result;
}
private void lock(){
logger.info("lock object...");
}
private void unlock(){
logger.info("unlock object...");
}
}
上述程式碼中並沒有出現與具體應用層相關的介面以及類的引用,所以對所有的類都適用。這便解決了用普通Proxy類實現的弊端。但是動態代理要求所代理的類必須是某個介面的實現(這點可以通過obj.getClass().getInterfaces()看出),不過這也符合物件導向的設計思想,如果所代理的類沒有實現任何介面,可以通過GCLIB來實現,這裡就不再詳述。

 

最後我們寫下一個TestCase來測試動態代理的實現,如下:
public class DyproxyTestCase extends TestCase {
private LockHandler handler=null;
private DomainObjDAOImpl daoImpl=null;
protected void setUp() throws Exception {
// TODO Auto-generated method stub
super.setUp();
handler=new LockHandler();
daoImpl=new DomainObjDAOImpl();

}

protected void tearDown() throws Exception {
super.tearDown();
}
public void testSave(){
((DomainObjDAO)handler.bind(daoImpl)).save();
}
}

執行結果如下:
2004-12-1 23:01:10 test.aop.dynamicproxy.LockHandler bind
資訊: coming here...
2004-12-1 23:01:10 test.aop.dynamicproxy.LockHandler lock
資訊: lock object...
saving domain object......
2004-12-1 23:01:10 test.aop.dynamicproxy.LockHandler unlock
資訊: unlock object...


至此,我們用動態代理實現了AOP,Spring的AOP實現正是採用了動態代理,我將在下一個Blog中討論其實現。

 

轉自:領測軟體測試網[http://www.ltesting.net]
原文連結:http://www.ltesting.net/ceshi/ruanjianzhiliangbaozheng/mxdx/2007/0525/4505.html

相關文章