面向方面程式設計的Annotation簡介(轉)

post0發表於2007-09-06
面向方面程式設計的Annotation簡介(轉)[@more@]

  

  Annotation概述

  

  

  首先讓我們給出這兩項技術的一個概述。AnnotationJDK5.0的新功能,它在JSR-175規範中有詳細定義。它們允許您以安全的方法定義後設資料並應用到類,方法,構造程式,欄位或引數中。對於你們中熟悉XDoclet的人來說,Annotation將非常直觀,您可以用來宣告標籤以產生程式碼。兩者的主要不同是AnnotationJava語言的一部分而XDoclet標籤可能會打錯並且難以建立。我喜歡用例子來說明,所以讓我們展示一個簡單的例子。

  

  

  要定義一個Annotation,您所要做的就是宣告一個特殊型別的Java介面。

  

  

  清單1Orange.java

  
package org.jboss.collors;

  public @interface Orange{}

  

  

  定義了這個介面,您就可以用來提供更多的描述給您的Java元素。

  

  

  清單2Foo.java

  

  package org.jboss.examples;

  public class Foo

  {

  @Orange void someMethod();

  @Orange private int someField;

  }

  

  

  那麼我們可以用Annotation來幹什麼呢?一些人想用Annotation來產生程式碼並替代XDoclet,其他人,象J2EEEJB3.0專家組,將它視為部署描述符的替代。本文談論在AOP中如何使用Annotation

  

  

  AOP概述

  

  

  有許多的文章和書籍解釋AOP到底是什麼,例如Graham O'ReganONJava文章“Introduction to Aspect-Oriented Programming."我將在本文給出一個快速的概覽,但我鼓勵您線上做更多的研究。

  

  

  假設您要新增程式碼到一個應用程式去測試呼叫一個特定的java方法所需的總的時間。該程式碼可能看起來如下:

  

  

清單3

  

  public class BankAccount

  {

  public void withdraw(double amount)

  {

  long startTime = System.currentTimeMillis();

  try

  {

  // Actual method body...

  }

  finally

  {

  long endTime = System.currentTimeMillis() - startTime;

  System.out.println("withdraw took: " + endTime);

  }

  }

  }

  

  

  雖然這些程式碼能夠正常工作,但這個方法有一些問題:1.它難以開啟和關閉測試,您必須在try/finally塊中對每個方法或購置函式手工增加程式碼以進行基準測試。

  

  

  2.這一輪廓程式碼並不真正屬於貫穿整個應用的程式碼。它使得您的程式碼臃腫並難以理解,因為您必須將計時放在try/finally塊中。

  

  

  3、如果您想擴充套件它的功能以包含一個方法或是失敗計數,或甚至是註冊這些統計資料到一個更為複雜的報告機制中,您必須修改大量不同檔案(又一次)。

  

  

  Metrics類提供了一個什麼是橫切(cross-cutting)關係的完美,簡潔的小例子。Jboss AOP以一種含蓄的方式提供了一個簡單的方法來封裝和應用這樣的關係,這樣某些象度量操作程式碼不會弄亂您的編碼。讓我們稍為深入到Jboss AOP一些來看看如何實現。

  

  

  為了使用Jboss AOP封裝度量功能,您首先需要定義一個方面來指出該度量行為。

  

  

  清單4

  

  public class Metrics

  {

  public Object profile(MethodInvocation invocation) throws Throwable

  {

  long startTime = System.currentTimeMillis();

  try

  {

  return invocation.invokeNext();

  }

  finally

  {

  long endTime = System.currentTimeMillis() - startTime;

  java.lang.reflect.Method m = invocation.getMethod();

  System.out.println("method " + m.toString() +

  " time: " + endTime + "ms");

  } }

  }

  

  

  一個方面只是一個具有定義了您想要附加到您的物件模型的行為的普通Java類。這些方法的簽名必須返回一個java.lang.Object並且必須具有一個(並且只有一個)Jboss AOP 呼叫物件引數,它被用來封裝方法,建構函式或欄位呼叫。方法名可以是任何你想要的並且當您繫結該方面到您的程式碼片斷時被引用。

  

  

  下面要做的事情就是實際應用方面到您想要它勾勒一個方法的執行的某個程式點。大多數AOP框架提供了一個指向表示式語言,在此處您可以定義您想要某個方面行為被附加到的位置。下面是在Jboss AOP中的做法。

  

  

  清單5jboss-aop.xml

  

  

  

  

  

  

  

  

  

  

  採用在Metrics.java中對方面的定義和jboss-aop.xml中的指向定義,該度量程式碼現在以含蓄而又透明地應用到BankAccount.withdraw()方法中並能在勾勒程式碼不再需要時輕易地移除。

  

  

  對於Jboss AOP更多的資訊,請查詢分發包中的指南。其中具有大約20個例子來帶領您漫遊如何使用Jboss AOP框架。

  

  

  噓!現在我們已經進行了一個概覽,讓我們深入到本文的中心內容。我將再次給您提供一些例子,因為這是我所知道的講授一個新的概念的最好的方法。

  

  

  正如我前面說的,Annotation加上AOP幾乎是給予您擴充套件Java語言的能力。Annotation提供了宣告新的,可相容的,型別安全的語法機制。AOP提供了封裝和應用新的行為到一個語法表示式的機制。

  

  

  方法AnnotationAOP

  

  

  讓我們看看如何使用方法AnnotationAOP.使用AnnotationAOP並應用到一個方法類似於使用Javasynchronized關鍵字。當您設定一個方法為synchronized,您在告訴JVM:您想該方法在被呼叫時以一種特殊的方式進行。Annotation允許您定義一個新的關鍵字來觸發您自己的特殊的定製行為。AOP給予您封裝這一行為的能力並將其“編織”進該方法的執行中。再次的,這一概念的最佳描述是透過一個例子。

  

  

  讓我們假設我們想要新增新的語法,使用該語法使得我們可以在方法被標籤為@Oneway時,在後臺以另一個執行緒呼叫這個void方法。可以象這樣使用新的語法:

  

  

  清單6

  

  Import org.jboss.aspects.Oneway

  public class Foo

  {

  @Oneway public static void someMethord
(){…}

  public static void main(String[] args){

  somMethod();//executes in

  backgroud

  }

  }

  

  

  當someMethod()在main中被呼叫,它將非同步執行,這樣main中的程式碼可以並行執行其他任務。

  

  

  要實現這一功能,首先要在一個Annotation中為我們的@Oneway標籤定義新的Java語法。

  

  

清單7Oneway.java

  

  

package org.jboss.aspects;

  

  import java.lang.annotation.ElementType;

  import java.lang.annotation.Target;

  

  @Target({ElementType.METHOD})

  public @interface Oneway {}

  

  

夠簡單的。@Target標籤允許您縮小Annotation可以應用的地方。在本例中,我們的@OnewayAnnotation只能應用到一個方法。記住,這些都是J2SE5.0百分之百可用的純Java

  

  

下面要做的事是定義一個封裝我們的@Oneway行為的方面類。

  

  

清單8OnewayAspect.java

  

  

package org.jboss.aspects;

  

  public OnewayAspect

  {

  private static class Task implements Runnable

  {

  private MethodInvocation invocation;

  

  public Task(MethodInvocation invocation)

  {

  this.invocation = invocation;

  }

  public void run()

  {

  try { invocation.invokeNext(); }

  catch (Throwable ignore) { }

  }

  }

  

  public Object oneway(MethodInvocation invocation) throws Throwable

  {

  MethodInvocation copy = invocation.copy();

  Thread t = new Thread(new Task(copy));

  t.setDaemon(false);

  t.start();

  return null;

  }

  }

  

  

  這個方面夠簡單。oneway()方法複製invocation,建立一個執行緒,在後臺啟動整個呼叫並返回。我們可以想象一個更為複雜的例子:使用J2SE 5.0 java.util.concurrent包中的某些新的Executors,但這些程式碼很有希望闡明瞭如何基於這個例子構建更為複雜的實現。

  

  

  最後必須要做的事情是指定當@OnewayAnnotation在一個方法中宣告時觸發OnewayAspect應用的指向表示式。

  

  

清單9jboss-aop.xml

  

  

  

  

  

  aspect="org.jboss.aspects.OnewayAspect"/>

  

  

  

  

  該指向表示式規定任何具有@Oneway標籤的void方法都應該有OnewayAspect.oneway()方法在它本身執行前被執行。隨著Annotation,方面和現在定義的指向表示式,@Oneway語法現在可以用於您的應用程式中。一個簡單,清晰,易於實現的方法來擴充套件Java 語言!

  

  

  欄位AnnotationAOP

  

  

  讓我們看看如何使用欄位AnnotationAOP.使用AnnotationAOP,您可以改變一個物件的欄位或是作為一個類的靜態成員的實際儲存方式。在這個例子裡我們要完成的是當您將一個欄位(靜態或是成員)標記上@ThreadBased,儘管是將它儲存在java.lang.ThreadLocal,但它的值依然正常。當然,您可以直接使用ThreadLocal變數,但問題是ThreadLocal並非一個型別並且您必須使用“麻煩的”(好,它們並沒有那麼羅嗦)get()和set()方法。那麼我們現在做的就是建立一個ThreadLocal型別的欄位。我們主要的將建立一個稱為@Thradbased變數的新的Java欄位型別。

  

  

  象這樣使用新的型別:

  

  

清單10

  

  

import org.jboss.aspects.Threadbased;

  public class Foo

  {

  @Threadbased private int counter;

  }

  

  為了實現這個功能,我們必須先定義Annotation

  

  

清單11Threadbased.java

  

  

package org.jboss.aspects;

  import java.lang.annotation.ElementType;

  import java.lang.annotation.Target;

  @Target({ElementType.FIELD})

  public @interface Threadbased {}

  

  

  夠簡單。@Target標籤允許您縮小Annotation可以應用的地方。在本例中,我們的@ThreadbasedAnnotation只能應用到欄位。

  

  

  下面的事情是定義封裝我們的ThreadLocal行為的方面。

  

  

清單12ThreadbasedAspect.java

  

  

package org.jboss.aspects;

  import org.jboss.aop.joinpoint.*;

  import java.lang.reflect.Field;

  public class ThreadbasedAspect

  {

  private ThreadLocal threadbased = new ThreadLocal();

  public Object access(FieldReadInvocation invocation)

  throws Throwable

  {

  // just in case we have a primitive,

  // we can't return null

  if (threadbased.get() == null)

  return invocation.invokeNext();

  return threadbased.get();

  }

  public Object access(FieldWriteInvocation invocation)

  throws Throwable

  {

  threadbased.set(invocation.getValue());

  return null;

  }

  }

  

  

  ThreadbasedAspect 封裝到一個Java欄位的訪問。它裡面具有一個專門的ThreadLocal變數跟蹤thradlocal變為一個特殊的欄位。它還有一個單獨的access()方法,該方法根據一個欄位的getset方法是否被呼叫決定它是否被呼叫。這些方法委託給ThreadLocal來獲得欄位的當前值。

  

  

  最後,我們必須定義一個指向表示式,當@ThreadbasedAnnotation在某個欄位被指定時觸發ThreadbasedAspect的應用。

  

  

清單13jboss-aop.xml

  

  

  

  

  

  aspect="org.jboss.aspects.ThreadbasedAspect"/>

  

  

  

  

  只有當我們具有多個@Threadbased變數定義在同一個類時,我們需要為每個靜態欄位分配一個ThreadbasedAspect例項。對於成員變數,我們需要為每個欄位,每個物件例項分配一個ThreadbasedAspect例項。為了促進這一行為,方面定義透過設定例項為PER_JOINPOINT限制方面類的例項何時和何地被分配出去的範圍。如果我們不做限制,Jboss

  

  

  AOP會只分配一個

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-968168/,如需轉載,請註明出處,否則將追究法律責任。

相關文章