<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Hello Java

     

    動態(tài)代理和AOP的一點學(xué)習(xí)心得


    一、最初的設(shè)計

    有一個經(jīng)營一家在線商店的客戶,在這個商店系統(tǒng)中,有一個供管理員使用的管理定單的類,代碼如下:

    1 、接口

    Interface OrderService{

    ?

    ?

    ??????? public boolean showOrders();// 察看定單

    }

    2、實現(xiàn)

    Class OrderServiceImp implements OrderService{

    public boolean showOrders(){

    ?? // 察看所有的定單

    }

    }

    3、定單系統(tǒng)的流程控制類

    class OrderSystem{

    void processOrder(OrderService service){// 定義了定單處理的流程

    // 做其它的業(yè)務(wù)

    ……

    …...

    // 顯示定單

    if(service.showOrders(){

    //do something

    }

    else{

    //do something

    }

    ……

    …...

    // 做其它的業(yè)務(wù)

    }

    4、整個商店系統(tǒng)的主類

    public class OnlineShop{

    public static void main(String args[]){

    // 做其它的業(yè)務(wù)

    ……….

    ………

    // 做定單業(yè)務(wù)

    OrderService service = new OrderServiceImp();

    OrderSystem.processOrder service);

    // 做其它的業(yè)務(wù)

    …….

    …...

    }

    ?

    }

    現(xiàn)在系統(tǒng)的問題是安全性不好,從管理系統(tǒng)登陸的用戶不需要驗證就可以察看定單。所以客戶希望在察看定單之前,能驗證一下身份,

    只有授權(quán)的用戶才能察看定單。

    這個問題很好辦,首先,增加一個類,用于安全驗證:

    class SecurityManager{

    public static boolean check(){

    // 進行身份驗證

    // 如果驗證成功,返回 true;

    // 如果驗證失敗,返回 false;

    }

    }

    然后,在 showOrders 方法中做如下的更改

    Class OrderServiceImp{

    public boolean showOrders(){

    if(SecurityManager.check()){

    // 如果驗證成功

    // 顯示定單

    return true;

    }

    else{

    // 如果驗證失敗

    // 做一些處理失敗的工作

    return false;

    }

    }

    }

    OrderSystem 類和 OnlineShop 類不需要更改。

    好,這樣就搞定了,很容易,不過,總是感覺哪里有些不妥 ………

    ?

    二、 Proxy 模式

    這個新的系統(tǒng)運行了一段時間,客戶有提了新的需求:

      我們有需要另外的一套銷售系統(tǒng),這個新系統(tǒng)的定單處理子系統(tǒng)在業(yè)務(wù)流程上和上面提到的定單系統(tǒng)是一模一樣的 …...

    聽到這里,我想 " 好,既然新系統(tǒng)的業(yè)務(wù)流程一樣,那么 OrderSystem 類應(yīng)該是可以重用的,工作量不大 ……."

    但是,客戶接著說 " 除了新系統(tǒng)不需要安全驗證功能。 "

      果然出問題了,原來的 OrderService 中,驗證的代碼和業(yè)務(wù)代碼都寫在一起了,根本沒法分開,除非再寫一個 OrderService ,他們的代碼大部分都是重復(fù)的,只是去掉了驗證的代碼。

      原來的設(shè)計問題在于:它違反了 單一職責(zé)原則 。驗證和顯示定單是不同的兩個職責(zé), OrderService 應(yīng)該只負責(zé)業(yè)務(wù),對于一些附加的功能,例如權(quán)限認證應(yīng)該是一無所知的。如何才能做到這一點呢? GOF 的書里已經(jīng)總結(jié)了一個解決此類問題的設(shè)計模式,就是 代理模式 (Proxy)

      使用代理模式重新設(shè)計的架構(gòu)如下:

    ???? 1. OrderServiceImp 建立一個代理類,這個類必須要繼承同樣的接口 OrderService

    ????? class ProxyOrderService implements OrderService{

    ???????? private OrderService target;

    ProxyOrderService(OrderService target){

    this.target = target;

    }

    public boolean showOrders(){

    if(SecurityManager.check()){

    // 如果驗證成功

    // 顯示定單,具體的工作委托給 OrderServiceImpl

    return target.showOrders();

    }

    else{

    // 如果驗證失敗

    // 做一些處理失敗的工作

    return false;

    }

    ????? }

       OrderServiceImp 類只要負責(zé)處理業(yè)務(wù)邏輯即可 , 不必關(guān)心例如驗證之類的附加問題,這些問題交給代理來處理好了。而且業(yè)務(wù)類很容易單獨的重用。

    ??? class OrderServiceImp implements OrderService{

    public boolean showOrders(){

    ?? // 察看所有的定單

    }

    ??? }

    ?? 2. 我們把新的商店系統(tǒng)的主類命名為 InnerShop

    ??? OnlineShop main 方法做如下的修改:

    ????? public class OnlineShop{

    public static void main(String args[]){

    // 做其它的業(yè)務(wù)

    ……….

    ………

    // 做定單業(yè)務(wù) , 帶有驗證

    OrderService service = new ProxyOrderService(new OrderServiceImp());

    ???????? OrderSystem.processOrder service);

    ??????? // 做其它的業(yè)務(wù)

    ?????? …….

    ?????? …...

    }

    }

    InnerShop main 方法這樣來寫:

    public class InnerShop{

    public static void main(String args[]){

    // 做其它的業(yè)務(wù)

    ……….

    ………

    // 做定單業(yè)務(wù),不需要驗證

    OrderService service = new OrderServiceImp();

    OrderSystem.processOrder service);

    // 做其它的業(yè)務(wù)

    …….

    …...

    }

    ?

    }

    這樣的話,整個 OrderSystem 類就可以做到重用了。

    ?

    三、新的問題

    這個系統(tǒng)運行了一段時間,工作得很好。所以客戶又追加了新的需求,需要在 OrderService 類中增加刪除定單功能,同樣,只有有管理權(quán)限的用戶才能刪除定單。有了上面的關(guān)于 Proxy 模式的知識,做這個很簡單:

    1、在接口中增加一個方法 removeOrder()

    Interface OrderService{

    public boolean showOrders();// 察看定單

    public boolean removeOrder(Order order);// 刪除定單

    }

    2、然后再實現(xiàn)它

    Class OrderServiceImp{

    public boolean showOrders(){

    // 顯示定單

    }

    ?

    public boolean removeOrder(Order order)

    // 刪除定單,成功返回 true, 失敗返回 false

    }

    3 、修改代理類

    class ProxyOrderService implements OrderService{

    ???????? private OrderService target;

    ProxyOrderService(OrderService target){

    this.target = target;

    }

    public boolean showOrders(){

    if(SecurityManager.check()){

    // 如果驗證成功

    // 顯示定單,具體的工作委托給 OrderServiceImpl

    return target.showOrders();

    }

    else{

    // 如果驗證失敗

    // 做一些處理失敗的工作

    return false;

    }

    ?

    }

    // 新增加的 removeOrder 方法

    public boolean removeOrder(){

    ?if(SecurityManager.check()){

    // 如果驗證成功

    // 刪除定單,具體的工作委托給 OrderServiceImpl

    return target.removeOrder();

    ?}

    ?else{

    // 如果驗證失敗

    // 做一些處理失敗的工作

    ?

    return false;

    }

    ???????

    ??? ???}

    問題是解決了,不過感覺上不太優(yōu)雅,而且也存在著潛在的隱患 …..

    哪里不對呢,看看驗證的代碼:

    if(SecurityManager.check()){

    如果驗證成功

    …...

    }

    else{

    // 如果驗證失敗

    …...

    }

    這樣的代碼重復(fù)了兩遍,如果用戶有需要增加業(yè)務(wù)方法,那么這段代碼還得一遍又一遍的重復(fù)。大量的重復(fù)代碼就意味著難于維護,而且,一旦某個新的業(yè)務(wù)方法忘記加上這段代碼,就會造成安全隱患。

    除此之外,還有什么隱患呢?想一下如果過幾天客戶有來找你,這次是希望給另一種業(yè)務(wù)-會員管理系統(tǒng)加上驗證,假定會員管理的接口是 MemberService ,那么我還需要增加一個 ProxyMemberService ,因為 MemberService 的業(yè)務(wù)方法和 OrderService 完全不同。如果業(yè)務(wù)逐漸增多,我們會發(fā)現(xiàn)我們陷入了一個 Proxy 的海洋里,

    ?

    AServiceImpl---------------ProxyAService

    BServiceImpl---------------ProxyBService

    ……………………………….

    ………………………………

    NServiceImpl--------------ProxyNService

    更可怕的是每一個 Proxy 里面,都到處散布著安全驗證代碼片斷。

    ?

    四、最后的設(shè)計

    現(xiàn)在來整理一下思路,看看問題究竟出在什么地方。

    我們再重新審視一下 " 身份驗證 " 這個功能,他在系統(tǒng)中到底處在什么位置呢?

    首先,它是與業(yè)務(wù)邏輯無關(guān)的一個單獨的模塊,不應(yīng)該放在處理業(yè)務(wù)邏輯的方法中,于是我們用了 Proxy 把它從處理業(yè)務(wù)邏輯的方法中分離出來了。

    但是這種分離并不徹底,因為這個 " 身份驗證 " 功能應(yīng)該是獨立于 業(yè)務(wù) 的,不管這類業(yè)務(wù)是關(guān)于定單的還是會員管理的,它應(yīng)該在后面無聲無息的運行,像 OrderService, MemberService 這些業(yè)務(wù)類都不應(yīng)該察覺到身份驗證功能的存在。

    ?

    這樣看來,使用代理模式的思路沒有錯,它確實可以把驗證功能和業(yè)務(wù)邏輯分開,但是我們還需要更高級些的東西,我希望有這樣一種代理類,它可以做為任何類的代理,即使這些類的接口完全不同。

    會有這樣的好東西嗎?從 Java1.3 版開始,提供了一種叫做動態(tài)代理的技術(shù),它的特點是可以做為任何類的代理,而且在運行期可以動態(tài)的改變被代理的對象,它會攔截被代理類的方法調(diào)用,然后可以神不知鬼不覺的在真實的業(yè)務(wù)方法前后增加一些操作,比如說驗證。那么我們來看看它是怎么實現(xiàn)的:

    1 、首先我們需要一個類,它實現(xiàn)了 java.lang.reflect.InvocationHandler 接口,我們可以把它稱作“攔截器 " ,這個接口只定義了一個方法:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

    proxy 是代理類, method 代表要調(diào)用的真實業(yè)務(wù)類的方法, args 是方法的參數(shù),這三個參數(shù)在運行期會由 JRE 傳入進來,我們只需要這樣做就可以了:

    public class Security Handler implements InvocationHandler{

    private Object target; // 被代理的類,即業(yè)務(wù)類

    public Security Handler(Object target){

    this.target = target;

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    Boolean checkOk = SecurityManager.check();

    if(checkOk){

    ?? return method.invoke(target, args);

         }

    ???????? else????

    ???????? {

    ?????????? return ret;

    ???????? }

    }

    ?

    public Object getTarget() {

    return target;

    }

    public void setTarget(Object target) {

    this.target = target;

    }

    }

    ?

    2. 我們還需要一個工廠,來提供 Proxy 對象

    public class ServiceProxyFactory{

    public static Object getProxy(Class type, InvocationHandler handler) throws Exception{

    //type :要被代理的類的 class 對象

    //handler :要使用的 InvocationHandler

    ?? // 根據(jù)被代理類的類型,得到一個 Proxy

    ??? Class proxyClass = Proxy.getProxyClass(type.getClassLoader(), new Class[]{type});

    ?// 得到這個 Proxy 的構(gòu)造函數(shù) ??

    Constructor cons = proxyClass.getConstructor(new Class[]{InvocationHandler.class});

    // 通過 Proxy 的構(gòu)造函數(shù)得到一個 Proxy 的實例,這個實例就是針對特定業(yè)務(wù)類的代理類

    Object obj = cons.newInstance(new Object[]{handler});

    return obj;

    }

    }

    ?

    . 使用這個代理類

    OnlineShop main 函數(shù)中,可以這樣寫:

    ?

    public static void main(String args){

    // 建立針對驗證服務(wù)的攔截器,并且指定這個攔截器攔截 OrderService 的方法調(diào)用

    Security Handler handler = new SecurityHandler (new OrderServiceImp ());

    // 得到針對使用驗證攔截器的 OrderService 代理

    OrderService service = ( OrderService ) ServiceProxyFactory. getProxy( OrderService .class, handler);

    OrderSystem.processOrder service);

    那么,如果我們需要為會員管理功能提供驗證機能,只需要這樣就可以:

    Security Handler handler = new SecurityHandler (new MemberServiceImp ());

    MemberService service = ( MemberService ) ServiceProxyFactory. getProxy MemberService .class, handler);

    MemberSystem.processMember service);

    ?

    更近一步,如果客戶有要求我們?yōu)檎麄€系統(tǒng)增加記錄日志功能,這個功能和安全驗證功能一樣,都是獨立于業(yè)務(wù)的,我們只要按照同樣的方法再建立一個 LogHandler 即可:

    LogHandler handler = new LogHandler(new OrderServiceimpl());

    OrderService service = ( OrderService ) ServiceProxyFactory. getProxy( OrderService .class, handler);

    OrderSystem.processOrder service);

    ?

    我們還可以做什么?

    我們還可以用一個容器把需要用附加另外的服務(wù)的類存放在 XXXHandler 中,還可以在一個 XML 文件中指定哪個 Handler 應(yīng)用于那些類,哪些業(yè)務(wù)方法

    再遠一些,我們希望攔截做得更透明一些,如果我們不希望看到類似:

    OrderService service = ( OrderService ) ServiceProxyFactory. getProxy( OrderService .class, handler);

    這樣的代碼,我們還可以設(shè)計成采用依賴注入的方式把 Proxy 類悄悄的替換進系統(tǒng)中。

    ?

    最后,我們來總結(jié)一下,我們究竟做了些什么,并且用精確的術(shù)語對我們所做的事情進行一下定義:

    . 首先,我們關(guān)注了一種問題,驗證或者日志,這樣的問題是散布在系統(tǒng)的各處,獨立與業(yè)務(wù)的,我們把這種問題叫做一個 " 橫切關(guān)注點 "

    2. 我們把像驗證這樣的問題集中在一處處理,這樣就形成了一個 " 方面 (ASPECT) "

    3. 然后我們用一個 SecurityHandler 實現(xiàn)了這個 方面, 我們把 SecurityHandler 叫做一個“ 增強( Advice)

    4. OrderService.showOrders() 這樣需要進行安全認證的方法,我們把它們叫做 “切入點 "

    5. 在運行期, Advice 會被動態(tài)的織入到切入點處,透明的增強了原有業(yè)務(wù)的功能。

    上面所做的事情,就可以看做是AOP的機制的一種基本實現(xiàn)。

    posted on 2006-06-14 20:38 Hello Java 閱讀(659) 評論(1)  編輯  收藏 所屬分類: Java

    評論

    # re: 動態(tài)代理和AOP的一點學(xué)習(xí)心得 2007-09-20 15:38 wuming

    這是我在網(wǎng)上找到的最好的一篇關(guān)于 AOP的實例講解。
    謝謝。  回復(fù)  更多評論   


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     

    導(dǎo)航

    統(tǒng)計

    常用鏈接

    留言簿(1)

    隨筆分類

    隨筆檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 久久亚洲精品无码网站| 波多野结衣中文字幕免费视频 | 亚洲大尺码专区影院| 亚洲国产香蕉人人爽成AV片久久 | 国产成人精品日本亚洲网址| 亚洲国产精品无码中文字| 日韩一品在线播放视频一品免费| 久久免费精品一区二区| 青青草97国产精品免费观看| 亚洲小说图区综合在线| 91亚洲精品麻豆| 久久精品亚洲精品国产色婷| 亚洲精品色婷婷在线影院| 女人18毛片免费观看| 波多野结衣免费在线| 57pao国产成视频免费播放| 国产一精品一AV一免费| 中文字幕的电影免费网站| 免费人成大片在线观看播放电影 | 在线精品免费视频无码的| 成人在线免费看片| 曰批全过程免费视频网址| 免费国产污网站在线观看15| 毛片无码免费无码播放| 秋霞人成在线观看免费视频| a毛片在线免费观看| 好猛好深好爽好硬免费视频| 成人无码区免费A∨直播| 国产va免费精品| 国产在线观a免费观看| 韩国免费A级毛片久久| 在线观看特色大片免费网站| 中国一级特黄高清免费的大片中国一级黄色片 | 亚洲美女一区二区三区| 99ri精品国产亚洲| 亚洲综合亚洲国产尤物| 亚洲福利电影一区二区?| 亚洲伊人久久大香线蕉啊| 亚洲a级成人片在线观看| 亚洲剧情在线观看| 狠狠色伊人亚洲综合网站色|