明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

完成基于Aspectwerkz的AOP開發(fā)

[摘要]一、AOP編程概覽   面向?qū)ο缶幊碳夹g(shù)進入軟件開發(fā)的主流對軟件的開發(fā)方式產(chǎn)生了極大的影響,開發(fā)者可以用一組實體以及這些實體之間的關(guān)系將系統(tǒng)形象地表示出來,這使得他們能夠設(shè)計出規(guī)模更大、更復(fù)雜的系統(tǒng),開發(fā)周期也比以前更短。OO開發(fā)的唯一問題是,它本質(zhì)上是靜態(tài)的,需求的細微變化就可能對開發(fā)進度造成重...

  一、AOP編程概覽

  面向?qū)ο缶幊碳夹g(shù)進入軟件開發(fā)的主流對軟件的開發(fā)方式產(chǎn)生了極大的影響,開發(fā)者可以用一組實體以及這些實體之間的關(guān)系將系統(tǒng)形象地表示出來,這使得他們能夠設(shè)計出規(guī)模更大、更復(fù)雜的系統(tǒng),開發(fā)周期也比以前更短。OO開發(fā)的唯一問題是,它本質(zhì)上是靜態(tài)的,需求的細微變化就可能對開發(fā)進度造成重大影響。

  Aspect-Oriented Programming(AOP)是對OO技術(shù)的補充和完善,它允許開發(fā)者動態(tài)地修改靜態(tài)的OO模型,構(gòu)造出一個能夠不斷增長以滿足新增需求的系統(tǒng),就象現(xiàn)實世界中的對象會在其生命周期中不斷改變自身,應(yīng)用程序也可以在發(fā)展中擁有新的功能。

  例如,許多人想必有過在開發(fā)簡單的Web應(yīng)用時將Servlet作為入口點的經(jīng)驗,即用Servlet接收HTML表單的輸入,經(jīng)過處理后返回給用戶。開始時的Servlet可能是非常簡單的,只有剛好滿足用戶需求的最少量的代碼。然而,隨著“第二需求”的實現(xiàn),例如實現(xiàn)異常處理、安全、日志等功能,代碼的體積就會增加到原來的三、四倍——之所以稱之為“第二需求”,是因為Servlet的基本功能是接受和處理用戶的請求,對于這個目標來說,日志、安全之類的機制并不是必不可少的。

  AOP允許動態(tài)地改變OO的靜態(tài)模型,不必修改原來的靜態(tài)模型也可以加入滿足第二需求所需的代碼(實際上,甚至連原來的源代碼也不需要)。更令人稱奇的是,后來加入的代碼往往可以集中在一個地方,而不必象單純使用OO時那樣將后來加入的代碼分散到整個模型。

  二、基本術(shù)語

  在介紹AOP開發(fā)實例之前,我們先來了解幾個標準的AOP術(shù)語,以便更好地掌握相關(guān)的概念。

  █ Cross-cutting concern

  在OO模型中,雖然大部份的類只有單一的、特定的功能,但它們通常會與其他類有著共同的第二需求。例如,當(dāng)線程進入或離開某個方法時,我們可能既要在數(shù)據(jù)訪問層的類中記錄日志,又要在UI層的類中記錄日志。雖然每個類的基本功能極然不同,但用來滿足第二需求的代碼卻基本相同。

  █ Advice

  它是指想要應(yīng)用到現(xiàn)有模型的附加代碼。在本例中,它是指線程進入或退出某個方法時要運行的日志代碼。

  █ Point-cut

  這個術(shù)語是指應(yīng)用程序中的一個執(zhí)行點,在這個執(zhí)行點上需要采用前面的cross-cutting concern。在本例中,當(dāng)線程進入一個方法時出現(xiàn)一個Point-cut,當(dāng)線程離開方法時又出現(xiàn)另一個Point-cut。

  █ Aspect

  Point-cut和advice結(jié)合在一起就叫做aspect。在下面的例子中,我們通過定義一個point-cut并給予適當(dāng)?shù)腶dvice加入了一個日志(logging)aspect。

  AOP還有其它許多特性和術(shù)語,例如引入(Introduction),即把接口/方法/域引入到現(xiàn)有的類——它極大地拓寬了開發(fā)者的想象力。不過本文只介紹一些最基本的持性,熟悉這里介紹的概念后,你再深入一步研究AOP的其它特性,看看如何在自己的開發(fā)環(huán)境中使用它們。

  三、現(xiàn)有的框架

  目前最成熟、功能最豐富的AOP框架當(dāng)數(shù)AspectJ,AspectJ已成為大多數(shù)其它框架跟從的標準。但是,AspectJ也走出了非同尋常的一步,它的實現(xiàn)為Java語言增添了新的關(guān)鍵詞。雖然新的語法并不難學(xué),但卻意味著我們必須換一個編譯器,還要重新配制編輯器,只有這樣才能適應(yīng)新的語法。在規(guī)模較大的開發(fā)組中,這些要求可能難以辦到,因為整個開發(fā)小組都會受到影響。由于語言本身的變化,開發(fā)小組把AOP技術(shù)引入到現(xiàn)有項目的學(xué)習(xí)周期隨之延長。

  現(xiàn)在我們需要的是這樣一個框架,它可以方便地引入,且不會對原來的開發(fā)和構(gòu)造過程產(chǎn)生任何影響。滿足這些要求的框架不止一個,例如JBoss AOP、Nanning、Aspectwerkz(AW)。本文選用的是Aspectwerkz,因為它可能是最容易學(xué)習(xí)的框架,也是最容易集成到現(xiàn)有項目的框架。

  Aspectwerkz由Jonas Boner和Alexandre Vasseur創(chuàng)建,它是目前最快速、功能最豐富的框架之一。雖然它還缺乏AspectJ的某些功能,但己足以滿足大多數(shù)開發(fā)者在許多情形下的需要。

  Aspectwerkz最令人感興趣的特性之一是它能夠以兩種不同的模式運行:聯(lián)機模式和脫機模式。在聯(lián)機模式下,AW直接干預(yù)屬于JVM的底層類裝入機制,截取所有的類裝入請求,對字節(jié)碼實施即時轉(zhuǎn)換。AW提供了干預(yù)類裝入過程的許多選項,另外還有一個替代bin/java命令的封裝腳本,這個腳本能夠根據(jù)Java版本和JVM能力自動生成一組可運行的配制。對于開發(fā)者,聯(lián)機模式有許多優(yōu)點,它能插入到任何類裝入器并在類裝入期間生成新的類。也就是說,我們不必手工修改應(yīng)用程序的類,只要按通常的方式部署即可。不過,聯(lián)機模式要求對應(yīng)用服務(wù)器進行額外的配制,有時這一要求可能很難滿足。

  在脫機模式下,生成類需要二個步驟。第一步是用標準的編譯器編譯,第二步是重點——以脫機模式運行AWcompiler編譯器,讓它處理新生成的類。編譯器將修改這些類的字節(jié)碼,根據(jù)一個XML文件的定義,在適當(dāng)?shù)膒oint-cut插入advice。脫機模式的優(yōu)點是AWcompiler生成的類能夠在任何JVM 1.3以上的虛擬機運行,本文下面要用的就是這種模式,因為它不需要對Tomcat作任何修改,只要對構(gòu)造過程稍作修改就可以照搬到大多數(shù)現(xiàn)有的項目。

  四、安裝

  本文將以一個簡單的Web應(yīng)用程序為例,它用Ant編譯,部署在Tomcat 4+ Servlet容器上。下面我們假定讀者己準備好上述環(huán)境,包括JVM 1.3+,同時Tomcat被設(shè)置成從webapps文件夾自動部署應(yīng)用,自動將WAR擴展到目錄(這是Tomcat默認的操作方式,因此只要你尚未修改Tomcat的運行方式,下面的范例可直接運行)。我們將把Tomcat的安裝位置稱為%TOMCAT_HOME%。

  ⑴ 從http://apectwerkz.codehaus.org/下載Aspectwerkz,解開壓縮到適當(dāng)?shù)奈恢谩N覀儗堰@個位置稱為%ASPECTWERKZ_HOME%。

 、 設(shè)置%ASPECTWERKZ_HOME%環(huán)境變量。

 、 將Aspectwerkz加入到PATH環(huán)境變量,即設(shè)置set PATH=%PATH%;%ASPECTWERKZ_HOME%inaspectwerkz

 、 下載本文的示范程序,將它放入%TOMCAT_HOME%webapps文件夾。

 、 將Aspectwerkz的運行時類加入到Tomcat的classpath。你可以將它的JAR文件放入示例應(yīng)用的WEB-INFlib文件夾,或放入%TOMCAT_HOME%commonlib。

   五、編譯示例應(yīng)用

  如果你想深入研究一下本文的示例應(yīng)用,可以解開WAR文件提取它的內(nèi)容。你會發(fā)現(xiàn)根目錄下有一個aspectwerkz.xml文件,構(gòu)造應(yīng)用時它會被復(fù)制到WEB-INF/classes目錄。Servlet和advice的源文件在WEB-INF/src目錄下,另外還有一個構(gòu)建這些類的ANT腳本。

  在運行這個示例程序之前,你還要對它進行后期編譯。下面是具體的操作步驟:

 、 在命令行窗口中,轉(zhuǎn)到解開WAR文件的目錄。

  ⑵ 輸入下面的命令調(diào)用AW編譯器:aspectwerkz -offline aspectwerkz.xml WEB-INF/classes -cp %TOMCAT_HOME%commonlibservlet.jar。如后期編譯順利通過,應(yīng)看到下面的輸出:

  ( 1 s )
  SUCCESS: WEB-INFclasses

  在構(gòu)建文件中有一個名稱為war的ANT任務(wù),你可以用它重新創(chuàng)建WAR文件。

  六、運行示例應(yīng)用

  首先啟動(或重新啟動)Tomcat,然后在瀏覽器中打開http://localhost:8080/demo/。

  頁面打開后,可以看到一個帶二個輸入框的HTML表單,一個輸入名字,一個輸入郵件地址。輸入一些數(shù)據(jù),然后點擊按鈕提交表單,出現(xiàn)一個頁面顯示出聯(lián)系人信息和一個指向聯(lián)系人清單的鏈接。

  七、代碼分析

  JSP頁面就不分析了,現(xiàn)在我們對它不感興趣。我們來看看AOPServlet的代碼。

  package example;

  import java.io.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  public class AOPServlet extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
  Person person = new Person();
  if (request.getParameter("name") != null) {
   person.setName(
   request.getParameter("name"));
  }
  if (request.getParameter("email") != null) {
   person.setEmail(
   request.getParameter("email"));
  }
  request.setAttribute("person", person);
  RequestDispatcher rd =request.getRequestDispatcher("/view.jsp");
  rd.forward(request, response);
  }
  }

  在這個例子中,Servlet的代碼己盡量精簡,只包含一些必不可少的代碼,如創(chuàng)建了一個綁定請求參數(shù)的對象等,但沒有持久化操作,不需要額外的imports,它只實現(xiàn)了作為Servlet必須實現(xiàn)的最基本的操作。

  然而,根據(jù)說明文檔的要求,這個應(yīng)用程序必須將所有Person類型的對象特久化,所以要為這個應(yīng)用程序加入一個aspect。為創(chuàng)建這個aspect,我們首先要創(chuàng)建一個aspectwerkz.xml文件并將該文件放入classpath指定的目錄。本文示例提供了一個簡單的例子,你可以用編輯器打開查看。

  aspectwerkz.xml的第一部份定義了可用的advice,我們可以根據(jù)需要加入任意數(shù)量的advice:

 。糰dvice-def name="persist" class="example.PersistenceAdvice" deployment-model="perJVM"/>

  在這個片段中,我們定義了一個名稱為persist的advice,它的類型是example.PersistenceAdvice。最后一個屬性定義了該advice的排它性,在這里它的值是perJVM,表示在每一個JVM中只創(chuàng)建該advice的一個實例(有關(guān)部署模式的更多說明,請參見Aspectwerkz的文檔。

  第二部份開始定義aspect,這里就是我們將advice映射到point-cut創(chuàng)建aspect的地方。

 。糰spect name="servlet">
 。紁ointcut-def name="all" type="method"
  pattern="* example.*Servlet.doGet(..)"/>
 。糱ind-advice pointcut="all">
 。糰dvice-ref name="persist"/>
 。/bind-advice>
 。/aspect>

  下面我們一行一行地分析這段代碼:

 、 我們創(chuàng)建了一個叫做servlet的aspect。如有必要,我們可以創(chuàng)建任意數(shù)量的aspect。

 、 在第二行,我們創(chuàng)建了一個叫做all的point-cut,它只適用于方法(type="method")。

 、 第三行我們用一個正則表達式規(guī)定了把advice應(yīng)用到哪里。在這個例子中,我們指出應(yīng)用advice的條件是:不管返回值的類型是什么(第一個“*”),名稱以servlet結(jié)尾(*servlet)且包含一個帶任意參數(shù)的doGet方法(doGet(..))的example包里面的類。

 、 在第四行,我們告訴Aspectwerkz編譯器要把后面的advice應(yīng)用到所有的point-cut。

 、 在這里我們聲明要使用的advice是persist。

  現(xiàn)在我們知道了如何映射point-cut與advice創(chuàng)建出aspect,下面來看看一個提供advice的類的實例。在映射文件中,我們注冊了一個example.PersistenceAdvice類型的advice,下面是該類型的源代碼:

  package example;

  import javax.servlet.http.*;
  import org.codehaus.aspectwerkz.advice.*;
  import org.codehaus.aspectwerkz.joinpoint.*;

  public class PersistenceAdvice extends AroundAdvice {
  public PersistenceAdvice() {
  super();
  }
  public Object execute(final JoinPoint joinPoint)
  throws Throwable {
  MethodJoinPoint jp =(MethodJoinPoint) joinPoint;
  final Object result = joinPoint.proceed();
  Object[] parameters = jp.getParameters();
  if (parameters[0] instanceof HttpServletRequest) {
   HttpServletRequest request =(HttpServletRequest) parameters[0];
   if (request.getAttribute("person") != null) {
    Person contact =(Person) request.getAttribute("person");
    ContactManager persistent = new ContactManager();
    String fileName =(request.getRealPath("/")+"contacts.txt");
    persistent.save(contact, fileName);
   }
  }
  return result;
  }
  }


  execute()方法的第一行很容易理解,就是盡量把它定型成最具體的類型,第二行或許是最重要的:因為我們想要運行該方法并檢查結(jié)果,所以必須調(diào)用proceed()。在下一部份,我們捕獲HttpServletRequest,提取由Servlet放入的對象(記住,此時doGet()方法己運行結(jié)束)。

  最后,我們創(chuàng)建一個名稱為ContactManager的類,它的功能是把Person的數(shù)據(jù)保存到一個文本文件。實際上,要把數(shù)據(jù)保存到XML文件、數(shù)據(jù)庫或其它持久化存儲機制也很方便。

  這里需要掌握的一點是,在設(shè)計應(yīng)用或建立原型的階段,Servlet并不知道未來會發(fā)生什么變化,第二階段的功能可以隨時加入,正因為如此,所以我們說應(yīng)用程序能夠在發(fā)展過程中學(xué)習(xí)新的能力,以后要添加新的功能非常方便。

  【結(jié)束語】 我們在前面的例子中試驗了一個簡單的應(yīng)用,將它部署到Tomcat,并用瀏覽器運行和測試它的功能。雖然這個應(yīng)用本身并無任何實際用途,但它示范和證實了一些非常有用的概念。想象一下,你將可以快速地建立原型,完成后再引入安全、日志、持久化、緩沖之類的Cross-cutting concern。不管原始應(yīng)用的規(guī)模有多大,你將能夠在十分鐘之內(nèi)輕松地為整個應(yīng)用加入日志功能!

  希望你能夠超越本文的簡單例子,去看看如何在自己的項目中采用AOP技術(shù)。熟悉AOP的概念當(dāng)然需要一定的時間,但肯定會得到回報,對于一個中等規(guī)模的項目,它會讓你省下數(shù)星期時間,或者少寫數(shù)千行重復(fù)的代碼。