鷹翔宇空

          學(xué)習和生活

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            110 Posts :: 141 Stories :: 315 Comments :: 1 Trackbacks
          引自:http://www.3doing.net/forums/dispbbs.asp?boardID=11&ID=138&page=1


          發(fā)貼心情?AOP研究

          為什么要區(qū)分J2EE容器和J2EE應(yīng)用系統(tǒng)?

            我們知道,J2EE應(yīng)用系統(tǒng)只有部署在J2EE容器中才能運行,那么為什么劃分為J2EE容器和J2EE應(yīng)用系統(tǒng)? 通過對J2EE容器運行機制的分析(見我的電子教材“EJB實用原理”),我們可以發(fā)現(xiàn):實際上J2EE容器分離了一般應(yīng)用系統(tǒng)的一些通用功能,例如事務(wù)機制、安全機制以及對象池或線程池等性能優(yōu)化機制。

            這些功能機制是每個應(yīng)用系統(tǒng)幾乎都需要的,因此可以從具體應(yīng)用系統(tǒng)中分離出來,形成一個通用的框架平臺,而且,這些功能機制的設(shè)計開發(fā)有一定難度,同時運行的穩(wěn)定性和快速性都非常重要,必須經(jīng)過長時間調(diào)試和運行經(jīng)驗積累而成,因此,形成了專門的J2EE容器服務(wù)器產(chǎn)品,如Tomcat JBoss、Websphere、WebLogic等。

            從J2EE系統(tǒng)劃分為J2EE容器和J2EE應(yīng)用系統(tǒng)兩個方面,我們已經(jīng)看到一種分散關(guān)注的思路(separation of concerns)。

          分散關(guān)注

            將通用需求功能從不相關(guān)類之中分離出來;同時,能夠使得很多類共享一個行為,一旦行為發(fā)生變化,不必修改很多類,只要修改這個行為就可以。

             AOP就是這種實現(xiàn)分散關(guān)注的編程方法,它將“關(guān)注”封裝在“方面”中。

          AOP是什么?

            AOP是OOP的延續(xù),是Aspect Oriented Programming的縮寫,意思是面向方面編程。AOP實際是GoF設(shè)計模式的延續(xù),設(shè)計模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說也是這種目標的一種實現(xiàn)。

            舉例:假設(shè)有在一個應(yīng)用系統(tǒng)中,有一個共享的數(shù)據(jù)必須被并發(fā)同時訪問,首先,將這個數(shù)據(jù)封裝在數(shù)據(jù)對象中,稱為Data Class,同時,將有多個訪問類,專門用于在同一時刻訪問這同一個數(shù)據(jù)對象。

            為了完成上述并發(fā)訪問同一資源的功能,需要引入鎖Lock的概念,也就是說,某個時刻,當有一個訪問類訪問這個數(shù)據(jù)對象時,這個數(shù)據(jù)對象必須上鎖Locked,用完后就立即解鎖unLocked,再供其它訪問類訪問。

            使用傳統(tǒng)的編程習慣,我們會創(chuàng)建一個抽象類,所有的訪問類繼承這個抽象父類,如下:

          abstract class Worker{

            abstract void locked();
            abstract void accessDataObject();
            abstract void unlocked();

          }


            缺點:


          • accessDataObject()方法需要有“鎖”狀態(tài)之類的相關(guān)代碼。


          • Java只提供了單繼承,因此具體訪問類只能繼承這個父類,如果具體訪問類還要繼承其它父類,比如另外一個如Worker的父類,將無法方便實現(xiàn)。


          • 重用被打折扣,具體訪問類因為也包含“鎖”狀態(tài)之類的相關(guān)代碼,只能被重用在相關(guān)有“鎖”的場合,重用范圍很窄。

            仔細研究這個應(yīng)用的“鎖”,它其實有下列特性:


          • “鎖”功能不是具體訪問類的首要或主要功能,訪問類主要功能是訪問數(shù)據(jù)對象,例如讀取數(shù)據(jù)或更改動作。


          • “鎖”行為其實是和具體訪問類的主要功能可以獨立、區(qū)分開來的。


          • “鎖”功能其實是這個系統(tǒng)的一個縱向切面,涉及許多類、許多類的方法。如下圖:

            因此,一個新的程序結(jié)構(gòu)應(yīng)該是關(guān)注系統(tǒng)的縱向切面,例如這個應(yīng)用的“鎖”功能,這個新的程序結(jié)構(gòu)就是aspect(方面)

            在這個應(yīng)用中,“鎖”方面(aspect)應(yīng)該有以下職責:

            提供一些必備的功能,對被訪問對象實現(xiàn)加鎖或解鎖功能。以保證所有在修改數(shù)據(jù)對象的操作之前能夠調(diào)用lock()加鎖,在它使用完成后,調(diào)用unlock()解鎖。

          AOP應(yīng)用范圍

            很明顯,AOP非常適合開發(fā)J2EE容器服務(wù)器,目前JBoss 4.0正是使用AOP框架進行開發(fā)。
            具體功能如下:
          Authentication 權(quán)限
          Caching 緩存
          Context passing 內(nèi)容傳遞
          Error handling 錯誤處理
          Lazy loading 懶加載
          Debugging  調(diào)試
          logging, tracing, profiling and monitoring 記錄跟蹤 優(yōu)化 校準
          Performance optimization 性能優(yōu)化
          Persistence  持久化
          Resource pooling 資源池
          Synchronization 同步
          Transactions 事務(wù)

          AOP有必要嗎?

            當然,上述應(yīng)用范例在沒有使用AOP情況下,也得到了解決,例如JBoss 3.XXX也提供了上述應(yīng)用功能,但是沒有使用AOP。

            但是,使用AOP可以讓我們從一個更高的抽象概念來理解軟件系統(tǒng),AOP也許提供一種有價值的工具。可以這么說:因為使用AOP結(jié)構(gòu),現(xiàn)在JBoss 4.0的源碼要比JBoss 3.X容易理解多了,這對于一個大型復(fù)雜系統(tǒng)來說是非常重要的。

            從另外一個方面說,好像不是所有的人都需要關(guān)心AOP,它可能是一種架構(gòu)設(shè)計的選擇,如果選擇J2EE系統(tǒng),AOP關(guān)注的上述通用方面都已經(jīng)被J2EE容器實現(xiàn)了,J2EE應(yīng)用系統(tǒng)開發(fā)者可能需要更多地關(guān)注行業(yè)應(yīng)用方面aspect。

          AOP具體實現(xiàn)

            AOP是一個概念,并沒有設(shè)定具體語言的實現(xiàn),它能克服那些只有單繼承特性語言的缺點(如Java),目前AOP具體實現(xiàn)有以下幾個項目:

            AspectJ (TM): 創(chuàng)建于Xerox PARC. 有近十年歷史,成熟
            缺點:過于復(fù)雜;破壞封裝;需要專門的Java編譯器。

            動態(tài)AOP:使用JDK的動態(tài)代理API或字節(jié)碼Bytecode處理技術(shù)。

           動態(tài)AOP:使用JDK的動態(tài)代理API或字節(jié)碼Bytecode處理技術(shù)。

            基于動態(tài)代理API的具體項目有:
            JBoss 4.0 JBoss 4.0服務(wù)器
            nanning 這是以中國南寧命名的一個項目,搞不清楚為什么和中國相關(guān)?是中國人發(fā)起的?

            基于字節(jié)碼的項目有:
            aspectwerkz 
            spring 
          在以后其它文章中,我將繼續(xù)對AOP概念進行分析,和大家一起學(xué)習進步。

          發(fā)貼心情?

          AOP和AspectJ

          板橋里人http://www.jdon.com 2004/01/10

          需求和問題

            以上篇《AOP是什么》中并發(fā)訪問應(yīng)用為例子:

            多個訪問類同時訪問一個共享數(shù)據(jù)對象時,每個訪問類在訪問這個數(shù)據(jù)對象時,需要將數(shù)據(jù)對象上鎖,訪問完成后,再實行解鎖,供其它并發(fā)線程訪問,這是我們處理并發(fā)訪問資源的方式。

            為了實現(xiàn)這個需求,先實現(xiàn)傳統(tǒng)的編程,這里我們假定有一個寫鎖,對數(shù)據(jù)對象實行寫之前,首先對這個對象進行上寫鎖,寫操作完畢后,必須釋放寫鎖。

            首先,我們需要一個鎖,這個鎖可以是數(shù)據(jù)對象中一個字段或其它,這里使用Doug Lea的ReentrantWriterPreferenceReadWriteLock作為我們的鎖資源。

          import EDU.oswego.cs.dl.util.concurrent.*;

          public class Worker extends Thread {

            Data data;

            ReentrantWriterPreferenceReadWriteLock rwl = 
              new ReentrantWriterPreferenceReadWriteLock();

            public boolean createData() {
             try {
              rwl.writeLock().acquire(); //上鎖

              //對data實行寫邏輯操作 
                 
             }catch() {
               return false;
             }finally{
               rwl.writeLock().release();  //解鎖
             }
             return true;
            }

            public boolean updateData() {
             try {
              rwl.writeLock().acquire();//上鎖

              //對data實行寫邏輯操作 
                 
             }catch() {
               return false;
             }finally{
               rwl.writeLock().release(); //解鎖
             }
             return true;
            }

            public void run() {
              //執(zhí)行createData()或updateData()
            }
          }

          假設(shè)可能存在另外一個訪問類,也將對數(shù)據(jù)對象實現(xiàn)寫操作,代碼如下:

          import EDU.oswego.cs.dl.util.concurrent.*;

          public class AnotherWorker extends Thread {

            Data data;

            ReentrantWriterPreferenceReadWriteLock rwl = 
              new ReentrantWriterPreferenceReadWriteLock();
            
            public boolean updateData() {
             try {
              rwl.writeLock().acquire();//上鎖

              //對data實行寫邏輯操作 
                 
             }catch() {
               return false;
             }finally{
               rwl.writeLock().release(); //解鎖
             }
             return true;
            }

            public void run() {
              //執(zhí)行updateData()
            }
          }

            以上是Java傳統(tǒng)編程的實現(xiàn),這種鎖的實現(xiàn)方式是在每個具體類中實現(xiàn),如下圖:

          這種實現(xiàn)方式的缺點很多:

          • 冗余:有很多重復(fù)的編碼,如rwl.writeLock().acquire()等;
          • 減少重用:worker的updateData()方法重用性幾乎為零。
          • "數(shù)據(jù)對象寫操作必須使用鎖控制這個設(shè)計目的"不容易顯現(xiàn),如果更換了一個新的程序員,他可能編寫一段不使用鎖機制就對這個數(shù)據(jù)對象寫操作的代碼。
          • 如果上述代碼有讀功能,那么我們需要在代碼中實現(xiàn)先上讀鎖,當需要寫時,解讀鎖,再上寫鎖等等,如果稍微不小心,上鎖解鎖次序搞錯,系統(tǒng)就隱含大的BUG,這種可能性會隨著這個數(shù)據(jù)對象永遠存在下去,系統(tǒng)設(shè)計大大的隱患啊!

            那么我們使用AOP概念來重新實現(xiàn)上述需求,AOP并沒有什么新花招,只是提供了觀察問題的一個新視角度。

            這里我們可以拋開新技術(shù)迷人霧障,真正核心還是新思維、新視點,人類很多問題如果換一個腦筋看待理解,也許結(jié)果真的是翻天覆地不一樣啊,所以,作為人自身,首先要重視和你世界觀和思維方式不一樣的人進行交流和溝通。

            現(xiàn)實生活中有很多"不公平",例如某個小學(xué)畢業(yè)生成了千萬富翁,你就懷疑知識無用,也許你認為他的機會好,其實你可能不知道,他的觀察問題的視角比你獨特,或者他可能會經(jīng)常換不同的角度來看待問題和解決問題,而你由于過分陷入一個視角的具體實現(xiàn)細節(jié)中,迷失了真正的方向,要不說是讀書人腦子僵化呢?

            言歸正傳,我們看看AOP是如何從一個新的視角解決上述問題的。

            如果說上面代碼在每個類中實現(xiàn)上鎖或解鎖,類似橫向解決方式,那么AOP是從縱向方面來解決上述問題,縱向解決之道示意圖如下:

            AOP把這個縱向切面cross-cuts稱為Aspect(方面),其實我認為AOP翻譯成面向切面編程比較好,不知哪個糊涂者因為先行一步,翻譯成“面向方面編程”如此抽象,故弄玄虛。

          AspectJ實現(xiàn)

            下面我們使用AOP的實現(xiàn)之一AspectJ來對上述需求改寫。AspectJ是AOP最早成熟的Java實現(xiàn),它稍微擴展了一下Java語言,增加了一些Keyword等,pointcut的語法如下:

          public pointcut 方法名:call(XXXX)

            AspectJ增加了pointcut, call是pointcut類型,有關(guān)AspectJ更多基本語法見這里。因為AspectJ使用了一些特別語法,所以Java編譯器就不能用SUN公司提供javac了,必須使用其專門的編譯器,也許SUN在以后JDK版本中會引入AOP。

            使用AspectJ如何實現(xiàn)上圖所謂切面式的編程呢?首先,我們將上圖縱向切面稱為Aspect,那么我們建立一個類似Class的Aspect,Java中建立一個Class代碼如下:

          public class MyClass{
            //屬性和方法 ...
          }

            同樣,建立一個Aspect的代碼如下:

          public aspect MyAspect{
            //屬性和方法 ...
          }

          建立一個Aspect名為Lock,代碼如下:

          import EDU.oswego.cs.dl.util.concurrent.*;

          public aspect Lock {

            ......
            ReentrantWriterPreferenceReadWriteLock rwl = 
              new ReentrantWriterPreferenceReadWriteLock();

            public pointcutwriteOperations():
              execution(public boolean Worker.createData()) ||
              execution(public boolean Worker.updateData()) ||
              execution(public boolean AnotherWorker.updateData()) ;


            before() : writeOperations() {
              rwl.writeLock().acquire();//上鎖 advice body
            }

            after() : writeOperations() {
               rwl.writeLock().release(); //解鎖 advice body
            }

            ......
          }

            上述代碼關(guān)鍵點是pointcut,意味切入點或觸發(fā)點,那么在那些條件下該點會觸發(fā)呢?是后面紅字標識的一些情況,在執(zhí)行Worker的createData()方法,Worker的update方法等時觸發(fā)。

            before代表觸發(fā)之前做什么事情?
            答案是上鎖。

            after代表觸發(fā)之后做什么事情?
            答案是上鎖。

            通過引入上述aspect,那么Worker代碼可以清潔如下:

          public class Worker extends Thread {

            Data data;

            public boolean createData() {
             try {
              //對data實行寫邏輯操作        
             }catch() {
               return false;
             }
             return true;
            }

            public boolean updateData() {
             try {
              //對data實行寫邏輯操作        
             }catch() {
               return false;
             }finally{
             }
             return true;
            }

            public void run() {
              //執(zhí)行createData()或updateData()
            }
          }

            Worker中關(guān)于“鎖”的代碼都不見了,純粹變成了數(shù)據(jù)操作的主要方法。

          AOP術(shù)語

            通過上例已經(jīng)知道AspectJ如何從切面crosscutting來解決并發(fā)訪問應(yīng)用需求的,其中最重要的是引入了一套類似事件觸發(fā)機制。

            Pointcut類似觸發(fā)器,是事件Event發(fā)生源,一旦pointcut被觸發(fā),將會產(chǎn)生相應(yīng)的動作Action,這部分Action稱為Advice。

            Advice在AspectJ有三種:before、 after、Around之分,上述aspect Lock代碼中使用了Advice的兩種before和after。

            所以AOP有兩個基本的術(shù)語:Pointcut和Advice。你可以用事件機制的Event和Action來類比理解它們。上述并發(fā)訪問應(yīng)用中pointcut和advice如下圖所示:

          小結(jié)如下:
          advice - 真正的執(zhí)行代碼,或者說關(guān)注的實現(xiàn)。 類似Action。
          join point - 代碼中激活advice被執(zhí)行的觸發(fā)點。
          pointcut - 一系列的join point稱為pointcut,pointcut有時代指join point

          其中advice部分又有:
          Interceptor - 解釋器并沒有在AspectJ出現(xiàn),在使用JDK動態(tài)代理API實現(xiàn)的AOP框架中使用,解釋有方法調(diào)用或?qū)ο髽?gòu)造或者字段訪問等事件,是調(diào)用者和被調(diào)用者之間的紐帶,綜合了Decorator/代理模式甚至職責鏈等模式。

          Introduction - 修改一個類,以增加字段、方法或構(gòu)造或者執(zhí)行新的接口,包括Mixin實現(xiàn)。

          例如上述并發(fā)訪問應(yīng)用中,如果想為每個Data對象生成相應(yīng)的aspect Lock,那么可以在aspect Lock中人為數(shù)據(jù)對象增加一個字段lock,如下:

          aspect Lock {

            Data sharedDataInstance;
            Lock( Data d ) {
               sharedDataInstance = d;
            }

            introduce Lock Data.lock; //修改Data類,增加一字段lock

            advise Data() { //Data構(gòu)造時觸發(fā)
               static after {
                 //當Data對象生成時,將Data中l(wèi)ock字段賦值為aspect Lock
                 //為每個Data對象生成相應(yīng)的aspect Lock
                 thisObject.lock = new Lock( thisObject );
               }
             }
            ....

          }

          上述代碼等于在Data類中加入一行:

          public class Data{
            ......
            Lock lock = new Lock();
            ......
          }

          還有其它兩個涉及AOP代碼運行方式:

          weaving - 將aspect代碼插入到相應(yīng)代碼中的過程,一般是編譯完成或在運行時動態(tài)完成。取決于具體AOP產(chǎn)品,例如AspectJ是使用特殊編譯器在編譯完成weaving,而nanning、JBoss AOP是使用動態(tài)代理API,因此在運行時動態(tài)完成weaving的。
          instrumentor - 用來實現(xiàn)weaving功能的工具。

          發(fā)貼心情?

          AOP與權(quán)限控制實現(xiàn)

          板橋里人http://www.jdon.com 2004/01/10

            以往在J2EE系統(tǒng)中,訪問權(quán)限控制系統(tǒng)的實現(xiàn)主要有兩種:應(yīng)用程序?qū)崿F(xiàn)和J2EE容器實現(xiàn)。

          傳統(tǒng)的應(yīng)用程序?qū)崿F(xiàn)

            這是最直接的、傳統(tǒng)的一種解決方式,通常是在具體方法前加一個權(quán)限判斷語句,如下:

          public class ForumFactoryProxy extends ForumFactory {
            ......
            public Forum createForum(String name, String description)
              throws UnauthorizedException, ForumAlreadyExistsException
            {
              if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {
                Forum newForum = factory.createForum(name, description);
                return new ForumProxy(newForum, authorization, permissions);
              }else {
                throw new UnauthorizedException();
              }
            }
            ......
          }

            上述代碼是Jive論壇中一段創(chuàng)建論壇功能的代碼,在創(chuàng)建論壇前,首先進行權(quán)限角色檢驗,如果當前用戶是系統(tǒng)管理員,那么可以實現(xiàn)真正的創(chuàng)建。

            這種在具體功能前加入權(quán)限操作檢驗的實現(xiàn)方式有很多缺點:
            1.每個功能類都需要相應(yīng)的權(quán)限檢驗代碼,將程序功能和權(quán)限檢驗混淆在一起,存在緊密的耦合性,擴展修改難度大。
            2.如果類似Jive,以代理模式為每個功能類實現(xiàn)一個相應(yīng)的代理類,雖然解耦了程序功能和權(quán)限檢驗,但是,從某個角色的權(quán)限檢驗這個切面考慮,涉及具體Proxy類太多,擴展修改難度大。

          J2EE容器實現(xiàn)

            在AOP概念沒有誕生前,J2EE規(guī)范已經(jīng)提供了關(guān)于權(quán)限控制的容器實現(xiàn)標準,這種變遷結(jié)果如下圖所示:

            原來需要每個應(yīng)用程序?qū)崿F(xiàn)的權(quán)限Proxy轉(zhuǎn)為整個容器的Proxy實現(xiàn),其中JDK1.3以后的動態(tài)代理API為這種轉(zhuǎn)換實現(xiàn)提供了技術(shù)保證。

            非常明顯,通過容器實現(xiàn)權(quán)限控制驗證可以大大簡化應(yīng)用程序的設(shè)計,分離了應(yīng)用系統(tǒng)的權(quán)限關(guān)注,將權(quán)限控制變成了對J2EE容器服務(wù)器的配置工作,具體技術(shù)細節(jié)參考我的書籍《Java實用系統(tǒng)開發(fā)指南》第六章。

            其實,容器的權(quán)限實現(xiàn)也是一種從一個切面來解決問題方式,AOP概念誕生后,權(quán)限控制實現(xiàn)由此也帶來了兩個方向的變化:
            1. J2EE容器級別的權(quán)限實現(xiàn),也就是容器自身的權(quán)限實現(xiàn)。
            2. J2EE應(yīng)用程序級別的權(quán)限實現(xiàn)。

            權(quán)限控制在容器級別實現(xiàn)似乎使得J2EE開發(fā)者感覺沒有靈活性和可擴展性,其實象JBoss 4.0這樣的J2EE容器,由于引入了AOP概念,使得J2EE開發(fā)者在自己的應(yīng)用系統(tǒng)中能夠直接操縱容器的一些行為。容器和應(yīng)用系統(tǒng)由于AOP引入的Aspect切面,變得可以成為一體了。(如果使用BEA的EJBC編輯要浪費多少時間?)

            對于J2EE應(yīng)用系統(tǒng)開發(fā)者,能夠做到上述境界,必須的條件是對JBoss之類J2EE容器必須有足夠的了解,因為這些方式并不是J2EE標準,有可能在移植到新的J2EE容器,這些知識和投入變得無用(也有可能將來J2EE擴展其標準)。

            很顯然,使用AOP實現(xiàn)J2EE應(yīng)用系統(tǒng)級別的權(quán)限控制,是解決上述移植風險的一個主要方法,但是帶來的缺點是必須親自從零開始做起,耗費時間不會很短。

          AOP下的應(yīng)用程序權(quán)限控制實現(xiàn)

            引入AOP概念后的權(quán)限實現(xiàn)已經(jīng)不是前面Jive實例那樣“落后”,我們對這個實例進行重整(Refactorying)如下:

          創(chuàng)建一個Aspect,專門用于權(quán)限檢查,如下:

          private static aspect PermissionCheckAspect {

            private pointcut permissionCheckedExecution() :
             execution ( public Forum ForumFactory.createForum(String , String ));

            before () : permissionCheckedExecution() {
              if !(permissions.get(ForumPermissions.SYSTEM_ADMIN)) {
                throw new UnauthorizedException();
              }
             }

          }

          該段代碼功能是:當系統(tǒng)運行ForumFactory.createForum方法之前,將首先檢查是否有權(quán)限操作。

          代碼中pointcut觸發(fā)的條件是createForum方法執(zhí)行,如果有其它需要系統(tǒng)管理員身份才能執(zhí)行的方法加入,將寫成如下代碼:

          private pointcut permissionCheckedExecution() :
             execution ( public Forum ForumFactory.createForum(String , String )) ||
             execution ( public Forum ForumFactory.deleteForum(String , String )) ||
             ......
             execution ( public Forum ForumFactory.deleteThread(String , String ));

          這些方法陳列比較瑣碎,依據(jù)AspectJ語法,可以簡化如下:
          private pointcut permissionCheckedExecution() :
             execution ( public * ForumFactory .*(..));

          有興趣者可以將Jive論壇中相關(guān)權(quán)限Proxy部分使用AOP重整,另外,由于Jive沒有引入角色概念,導(dǎo)致權(quán)限和用戶HardCode在編碼中,如何實現(xiàn)權(quán)限和用戶解耦,最小限度的降低HardCode量,角色概念在其中起著不可忽視的重要作用。這是另外一個研究課題了。

          發(fā)貼心情?

          Ioc模式

          板橋里人http://www.jdon.com 2004/01/31

            分離關(guān)注( Separation of Concerns : SOC)是Ioc模式和AOP產(chǎn)生最原始動力,通過功能分解可得到關(guān)注點,這些關(guān)注可以是 組件Components, 方面Aspects或服務(wù)Services。

            從GoF設(shè)計模式中,我們已經(jīng)習慣一種思維編程方式:Interface Driven Design 接口驅(qū)動,接口驅(qū)動有很多好處,可以提供不同靈活的子類實現(xiàn),增加代碼穩(wěn)定和健壯性等等,但是接口一定是需要實現(xiàn)的,也就是如下語句遲早要執(zhí)行:

            AInterface a = new AInterfaceImp();

            AInterfaceImp是接口AInterface的一個子類,Ioc模式可以延緩接口的實現(xiàn),根據(jù)需要實現(xiàn),有個比喻:接口如同空的模型套,在必要時,需要向模型套注射石膏,這樣才能成為一個模型實體,因此,我們將人為控制接口的實現(xiàn)成為“注射”。

            Ioc英文為 Inversion of Control,即反轉(zhuǎn)模式,這里有著名的好萊塢理論:你呆著別動,到時我會找你。

            其實Ioc模式也是解決調(diào)用者和被調(diào)用者之間的一種關(guān)系,上述AInterface實現(xiàn)語句表明當前是在調(diào)用被調(diào)用者AInterfaceImp,由于被調(diào)用者名稱寫入了調(diào)用者的代碼中,這產(chǎn)生了一個接口實現(xiàn)的原罪:彼此聯(lián)系,調(diào)用者和被調(diào)用者有緊密聯(lián)系,在UML中是用依賴 Dependency 表示。

            但是這種依賴在分離關(guān)注的思維下是不可忍耐的,必須切割,實現(xiàn)調(diào)用者和被調(diào)用者解耦,新的Ioc模式 Dependency Injection 模式由此產(chǎn)生了, Dependency Injection模式是依賴注射的意思,也就是將依賴先剝離,然后在適當時候再注射進入。

          Ioc模式(Dependency Injection模式)有三種:

          第一種類型從JNDI或ServiceManager等獲得被調(diào)用者,這里類似ServiceLocator模式。1. EJB/J2EE
          2. Avalon(Apache的一個復(fù)雜使用不多的項目)
          第二種類型使用JavaBeans的setter方法1. Spring Framework,
          2. WebWork/XWork
          第三種類型在構(gòu)造方法中實現(xiàn)依賴1. PicoContainer,
          2. HiveMind

            有過EJB開發(fā)經(jīng)驗的人都知道,每個EJB的調(diào)用都需要通過JNDI尋找到工廠性質(zhì)的Home接口,在我的教程EJB是什么章節(jié)中,我也是從依賴和工廠模式角度來闡述EJB的使用。

            在通常傳統(tǒng)情況下,為了實現(xiàn)調(diào)用者和被調(diào)用者解耦,分離,一般是通過工廠模式實現(xiàn)的,下面將通過比較工廠模式和Ioc模式不同,加深理解Ioc模式。

          工廠模式和Ioc

            假設(shè)有兩個類B 和 C:B作為調(diào)用者,C是被調(diào)用者,在B代碼中存在對C的調(diào)用:

          public class B{
             private C comp;
            ......
          }

            實現(xiàn)comp實例有兩種途徑:單態(tài)工廠模式和Ioc。

          工廠模式實現(xiàn)如下:

          public class B{
             private C comp;
            private final static MyFactory myFactory = MyFactory.getInstance();

            public B(){
              this.comp = myFactory.createInstanceOfC();

            }
             public void someMethod(){
              this.comp.sayHello();
            }
            ......
          }

          特點:

          • 每次運行時,MyFactory可根據(jù)配置文件XML中定義的C子類實現(xiàn),通過createInstanceOfC()生成C的具體實例。

          使用Ioc依賴性注射( Dependency Injection )實現(xiàn)Picocontainer如下,B類如同通常POJO類,如下:

          public class B{
             private C comp;
            public B(C comp){
              this.comp = comp;
             }
             public void someMethod(){
              this.comp.sayHello();
             }
            ......
          }

          假設(shè)C接口/類有有一個具體實現(xiàn)CImp類。當客戶端調(diào)用B時,使用下列代碼:

          public class client{
             public static void main( String[] args ) {
              DefaultPicoContainer container = new DefaultPicoContainer();
              container.registerComponentImplementation(CImp.class);
              container.registerComponentImplementation(B.class);
              B b = (B) container.getComponentInstance(B.class);
              b.someMethod();
             }
          }

            因此,當客戶端調(diào)用B時,分別使用工廠模式和Ioc有不同的特點和區(qū)別:

            主要區(qū)別體現(xiàn)在B類的代碼,如果使用Ioc,在B類代碼中將不需要嵌入任何工廠模式等的代碼,因為這些工廠模式其實還是與C有些間接的聯(lián)系,這樣,使用Ioc徹底解耦了B和C之間的聯(lián)系。

            使用Ioc帶來的代價是:需要在客戶端或其它某處進行B和C之間聯(lián)系的組裝。

            所以,Ioc并沒有消除B和C之間這樣的聯(lián)系,只是轉(zhuǎn)移了這種聯(lián)系。
            這種聯(lián)系轉(zhuǎn)移實際也是一種分離關(guān)注,它的影響巨大,它提供了AOP實現(xiàn)的可能。

          Ioc和AOP

            AOP我們已經(jīng)知道是一種面向切面的編程方式,由于Ioc解放自由了B類,而且可以向B類實現(xiàn)注射C類具體實現(xiàn),如果把B類想像成運行時的橫向動作,無疑注入C類子類就是AOP中的一種Advice,如下圖:

            通過下列代碼說明如何使用Picocontainer實現(xiàn)AOP,該例程主要實現(xiàn)是記錄logger功能,通過Picocontainer可以使用簡單一行,使所有的應(yīng)用類的記錄功能激活。

          首先編制一個記錄接口:

          public interface Logging {

            public void enableLogging(Log log);

          }

          有一個LogSwitcher類,主要用來激活具體應(yīng)用中的記錄功能:

          import org.apache.commons.logging.Log;
          public class LogSwitcher
          {
            protected Log m_log;
            public void enableLogging(Log log) {
              m_log = log;
              m_log.info("Logging Enabled");
            }
          }

          一般的普通應(yīng)用JavaBeans都可以繼承這個類,假設(shè)PicoUserManager是一個用戶管理類,代碼如下:

          public class PicoUserManager extends LogSwitcher
          {

            ..... //用戶管理功能
          }
          public class PicoXXXX1Manager extends LogSwitcher
          {

            ..... //業(yè)務(wù)功能
          }
          public class PicoXXXX2Manager extends LogSwitcher
          {

            ..... //業(yè)務(wù)功能
          }

          注意LogSwitcher中Log實例是由外界賦予的,也就是說即將被外界注射進入,下面看看使用Picocontainer是如何注射Log的具體實例的。


          DefaultPicoContainer container = new DefaultPicoContainer();
          container.registerComponentImplementation(PicoUserManager.class);
          container.registerComponentImplementation(PicoXXXX1Manager.class);
          container.registerComponentImplementation(PicoXXXX2Manager.class);
          .....

          Logging logging = (Logging) container.getComponentMulticaster();

          logging.enableLogging(new SimpleLog("pico"));//激活log

            由上代碼可見,通過使用簡單一行l(wèi)ogging.enableLogging()方法使所有的應(yīng)用類的記錄功能激活。這是不是類似AOP的advice實現(xiàn)?

            總之,使用Ioc模式,可以不管將來具體實現(xiàn),完全在一個抽象層次進行描述和技術(shù)架構(gòu),因此,Ioc模式可以為容器、框架之類的軟件實現(xiàn)提供了具體的實現(xiàn)手段,屬于架構(gòu)技術(shù)中一種重要的模式應(yīng)用。J道的JdonSD框架也使用了Ioc模式。




          posted on 2006-06-01 09:42 TrampEagle 閱讀(470) 評論(0)  編輯  收藏 所屬分類: java
          主站蜘蛛池模板: 宝丰县| 罗山县| 鹰潭市| 喀什市| 嘉黎县| 利津县| 华宁县| 班玛县| 龙游县| 广元市| 汉源县| 赤水市| 姚安县| 仲巴县| 威信县| 宁远县| 连云港市| 泗阳县| 永仁县| 双牌县| 道真| 连州市| 延边| 潍坊市| 祁连县| 永新县| 贺兰县| 左云县| 辉县市| 家居| 扶余县| 广东省| 柳州市| 天门市| 措勤县| 陆川县| 甘德县| 广宁县| 阿拉善左旗| 玉田县| 星座|