AOP簡(jiǎn)介
OOP思想對現代編程產(chǎn)生了深遠的影響,但在某些方面,OOP也有其不足之處。比如在logging(日志)、transaction(事務(wù))等方面,應用OOP將這些內容封裝為對象的行為則會(huì )產(chǎn)生大量的代碼重復,雖然通過(guò)一些設計模式可以減少這種重復,但我們還有更好的解決辦法,那就是AOP(Aspect Oriented Programming)。AOP是最近興起的一種編程思想,它是OOP思想的補充,而不是其對立面。
AOP,從字面的理解來(lái)看就是面向切面的編程,用一個(gè)比較通俗的例子來(lái)說(shuō),比如在訪(fǎng)問(wèn)多個(gè)對象前需要進(jìn)行權限檢查,那么如果按照面向對象的思路來(lái)說(shuō),權限檢查勢必會(huì )成為這多個(gè)對象的行為。如果每個(gè)對象都需要去實(shí)現這些行為,勢必會(huì )造成大量重復代碼的產(chǎn)生,寫(xiě)程序也會(huì )變得枯燥無(wú)味。但我們可以將權限檢查看作是一個(gè)切面,所有對這些對象的訪(fǎng)問(wèn)都要經(jīng)過(guò)這個(gè)切面。要了解AOP,就必須先了解幾個(gè)基本的且非常重要的概念。
Aspect(切面):對象操作過(guò)程中的截面。如權限檢查、日志、事務(wù)處理等。
Join Point(連接點(diǎn)):程序運行中的某個(gè)階段點(diǎn)。如某個(gè)方法調用,異常拋出等。
Advice(處理邏輯):某個(gè)連接點(diǎn)所采用的處理邏輯。
PointCut(切點(diǎn)):一系列連接點(diǎn)的集合,它指明Advice在什么時(shí)候被觸發(fā)。
示例
還是用例子來(lái)說(shuō)明一切,比如現在有一個(gè)DomainObjDAO接口以及其實(shí)現類(lèi)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方法中添加對該業(yè)務(wù)對象的鎖,比如在save前后加鎖和解鎖。拿到這個(gè)需求,在不影響外部調用邏輯以及不對現有代碼改動(dòng)的前提下,Proxy模式(GOF)是個(gè)不錯的選擇,新增一個(gè)Proxy類(lèi)同樣實(shí)現DomainObjDAO接口,在其實(shí)現方法中代理DomainObjDAOImpl類(lèi)的save方法,并在save的前后調用lock以及unlock方法。這種方法使得我們不必改動(dòng)外部調用邏輯以及現有代碼,但是如果有多個(gè)DomainObjImpl的情況下,該方法的弊端便暴露無(wú)遺,我們必須實(shí)現與DomainObjImpl個(gè)數相同的Proxy類(lèi)來(lái)實(shí)現該功能,這對我們來(lái)說(shuō)將是非??植狼也豢山邮艿?。
這個(gè)例子再次印證我們開(kāi)始所描述的,針對這類(lèi)問(wèn)題,OOP顯得有些力不從心,而AOP卻能很好的解決它,JDK1.3后所提供的動(dòng)態(tài)代理的特性為我們利用AOP的思想解決這個(gè)問(wèn)題提供了很好的思路,下面我們來(lái)看它如何實(shí)現。
動(dòng)態(tài)代理實(shí)現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...");
}
}
上述代碼中并沒(méi)有出現與具體應用層相關(guān)的接口以及類(lèi)的引用,所以對所有的類(lèi)都適用。這便解決了用普通Proxy類(lèi)實(shí)現的弊端。但是動(dòng)態(tài)代理要求所代理的類(lèi)必須是某個(gè)接口的實(shí)現(這點(diǎn)可以通過(guò)obj.getClass().getInterfaces()看出),不過(guò)這也符合面向對象的設計思想,如果所代理的類(lèi)沒(méi)有實(shí)現任何接口,可以通過(guò)GCLIB來(lái)實(shí)現,這里就不再詳述。
最后我們寫(xiě)下一個(gè)TestCase來(lái)測試動(dòng)態(tài)代理的實(shí)現,如下:
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...
至此,我們用動(dòng)態(tài)代理實(shí)現了AOP,Spring的AOP實(shí)現正是采用了動(dòng)態(tài)代理,我將在下一個(gè)Blog中討論其實(shí)現。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=201277
聯(lián)系客服