需求和問(wèn)題
以上篇《AOP是什么》中并發(fā)訪(fǎng)問(wèn)應用為例子:
多個(gè)訪(fǎng)問(wèn)類(lèi)同時(shí)訪(fǎng)問(wèn)一個(gè)共享數據對象時(shí),每個(gè)訪(fǎng)問(wèn)類(lèi)在訪(fǎng)問(wèn)這個(gè)數據對象時(shí),需要將數據對象上鎖,訪(fǎng)問(wèn)完成后,再實(shí)行解鎖,供其它并發(fā)線(xiàn)程訪(fǎng)問(wèn),這是我們處理并發(fā)訪(fǎng)問(wèn)資源的方式。
為了實(shí)現這個(gè)需求,先實(shí)現傳統的編程,這里我們假定有一個(gè)寫(xiě)鎖,對數據對象實(shí)行寫(xiě)之前,首先對這個(gè)對象進(jìn)行上寫(xiě)鎖,寫(xiě)操作完畢后,必須釋放寫(xiě)鎖。
首先,我們需要一個(gè)鎖,這個(gè)鎖可以是數據對象中一個(gè)字段或其它,這里使用Doug Lea的ReentrantWriterPreferenceReadWriteLock作為我們的鎖資源。
|
import EDU.oswego.cs.dl.util.concurrent.*; public class Worker extends Thread { Data data; |
假設可能存在另外一個(gè)訪(fǎng)問(wèn)類(lèi),也將對數據對象實(shí)現寫(xiě)操作,代碼如下:
|
import EDU.oswego.cs.dl.util.concurrent.*; public class AnotherWorker extends Thread { Data data; |
以上是Java傳統編程的實(shí)現,這種鎖的實(shí)現方式是在每個(gè)具體類(lèi)中實(shí)現,如下圖:

這種實(shí)現方式的缺點(diǎn)很多:
那么我們使用AOP概念來(lái)重新實(shí)現上述需求,AOP并沒(méi)有什么新花招,只是提供了觀(guān)察問(wèn)題的一個(gè)新視角度。
這里我們可以?huà)侀_(kāi)新技術(shù)迷人霧障,真正核心還是新思維、新視點(diǎn),人類(lèi)很多問(wèn)題如果換一個(gè)腦筋看待理解,也許結果真的是翻天覆地不一樣啊,所以,作為人自身,首先要重視和你世界觀(guān)和思維方式不一樣的人進(jìn)行交流和溝通。
現實(shí)生活中有很多"不公平",例如某個(gè)小學(xué)畢業(yè)生成了千萬(wàn)富翁,你就懷疑知識無(wú)用,也許你認為他的機會(huì )好,其實(shí)你可能不知道,他的觀(guān)察問(wèn)題的視角比你獨特,或者他可能會(huì )經(jīng)常換不同的角度來(lái)看待問(wèn)題和解決問(wèn)題,而你由于過(guò)分陷入一個(gè)視角的具體實(shí)現細節中,迷失了真正的方向,要不說(shuō)是讀書(shū)人腦子僵化呢?
言歸正傳,我們看看AOP是如何從一個(gè)新的視角解決上述問(wèn)題的。
如果說(shuō)上面代碼在每個(gè)類(lèi)中實(shí)現上鎖或解鎖,類(lèi)似橫向解決方式,那么AOP是從縱向方面來(lái)解決上述問(wèn)題,縱向解決之道示意圖如下:

AOP把這個(gè)縱向切面cross-cuts稱(chēng)為Aspect(方面),其實(shí)我認為AOP翻譯成面向切面編程比較好,不知哪個(gè)糊涂者因為先行一步,翻譯成“面向方面編程”如此抽象,故弄玄虛。
AspectJ實(shí)現
下面我們使用AOP的實(shí)現之一AspectJ來(lái)對上述需求改寫(xiě)。AspectJ是AOP最早成熟的Java實(shí)現,它稍微擴展了一下Java語(yǔ)言,增加了一些Keyword等,pointcut的語(yǔ)法如下:
public pointcut 方法名:call(XXXX)
AspectJ增加了pointcut, call是pointcut類(lèi)型,有關(guān)AspectJ更多基本語(yǔ)法見(jiàn)這里。因為AspectJ使用了一些特別語(yǔ)法,所以Java編譯器就不能用SUN公司提供javac了,必須使用其專(zhuān)門(mén)的編譯器,也許SUN在以后JDK版本中會(huì )引入AOP。
使用AspectJ如何實(shí)現上圖所謂切面式的編程呢?首先,我們將上圖縱向切面稱(chēng)為Aspect,那么我們建立一個(gè)類(lèi)似Class的Aspect,Java中建立一個(gè)Class代碼如下:
public class MyClass{
//屬性和方法 ...
}
同樣,建立一個(gè)Aspect的代碼如下:
public aspect MyAspect{
//屬性和方法 ...
}
建立一個(gè)Aspect名為L(cháng)ock,代碼如下:
|
import EDU.oswego.cs.dl.util.concurrent.*; public aspect Lock { public pointcut writeOperations(): after() : writeOperations() { ...... |
上述代碼關(guān)鍵點(diǎn)是pointcut,意味切入點(diǎn)或觸發(fā)點(diǎn),那么在那些條件下該點(diǎn)會(huì )觸發(fā)呢?是后面紅字標識的一些情況,在執行Worker的createData()方法,Worker的update方法等時(shí)觸發(fā)。
before代表觸發(fā)之前做什么事情?
答案是上鎖。
after代表觸發(fā)之后做什么事情?
答案是上鎖。
通過(guò)引入上述aspect,那么Worker代碼可以清潔如下:
|
public class Worker extends Thread { Data data; |
Worker中關(guān)于“鎖”的代碼都不見(jiàn)了,純粹變成了數據操作的主要方法。
AOP術(shù)語(yǔ)
通過(guò)上例已經(jīng)知道AspectJ如何從切面crosscutting來(lái)解決并發(fā)訪(fǎng)問(wèn)應用需求的,其中最重要的是引入了一套類(lèi)似事件觸發(fā)機制。
Pointcut類(lèi)似觸發(fā)器,是事件Event發(fā)生源,一旦pointcut被觸發(fā),將會(huì )產(chǎn)生相應的動(dòng)作Action,這部分Action稱(chēng)為Advice。
Advice在A(yíng)spectJ有三種:before、 after、Around之分,上述aspect Lock代碼中使用了Advice的兩種before和after。
所以AOP有兩個(gè)基本的術(shù)語(yǔ):Pointcut和Advice。你可以用事件機制的Event和Action來(lái)類(lèi)比理解它們。上述并發(fā)訪(fǎng)問(wèn)應用中pointcut和advice如下圖所示:

小結如下:
advice - 真正的執行代碼,或者說(shuō)關(guān)注的實(shí)現。 類(lèi)似Action。
join point - 代碼中激活advice被執行的觸發(fā)點(diǎn)。
pointcut - 一系列的join point稱(chēng)為pointcut,pointcut有時(shí)代指join point
其中advice部分又有:
Interceptor - 解釋器并沒(méi)有在A(yíng)spectJ出現,在使用JDK動(dòng)態(tài)代理API實(shí)現的AOP框架中使用,解釋有方法調用或對象構造或者字段訪(fǎng)問(wèn)等事件,是調用者和被調用者之間的紐帶,綜合了Decorator/代理模式甚至職責鏈等模式。
Introduction - 修改一個(gè)類(lèi),以增加字段、方法或構造或者執行新的接口,包括Mixin實(shí)現。
例如上述并發(fā)訪(fǎng)問(wèn)應用中,如果想為每個(gè)Data對象生成相應的aspect Lock,那么可以在aspect Lock中人為數據對象增加一個(gè)字段lock,如下:
|
aspect Lock { } |
上述代碼等于在Data類(lèi)中加入一行:
|
public class Data{
|
還有其它兩個(gè)涉及AOP代碼運行方式:
weaving - 將aspect代碼插入到相應代碼中的過(guò)程,一般是編譯完成或在運行時(shí)動(dòng)態(tài)完成。取決于具體AOP產(chǎn)品,例如AspectJ是使用特殊編譯器在編譯完成weaving,而nanning、JBoss AOP是使用動(dòng)態(tài)代理API,因此在運行時(shí)動(dòng)態(tài)完成weaving的。
instrumentor - 用來(lái)實(shí)現weaving功能的工具。
聯(lián)系客服