Junky's IT Notebook

          統計

          留言簿(8)

          積分與排名

          WebSphere Studio

          閱讀排行榜

          評論排行榜

          Spring interceptor

          1 Spring的通知類型

          現在讓我們看看Spring AOP是如何處理通知的。

          1.1. 通知的生命周期

          Spring的通知可以跨越多個被通知對象共享,或者每個被通知對象有自己的通知。這分別對應 per-classper-instance 通知。

          Per-class通知使用最為廣泛。它適合于通用的通知,如事務adisor。它們不依賴被代理 的對象的狀態,也不添加新的狀態。它們僅僅作用于方法和方法的參數。

          Per-instance通知適合于導入,來支持混入(mixin)。在這種情況下,通知添加狀態到 被代理的對象。

          可以在同一個AOP代理中混合使用共享和per-instance通知。

          1.2. Spring中通知類型

          Spring提供幾種現成的通知類型并可擴展提供任意的通知類型。讓我們看看基本概念和標準的通知類型。

          1.2.1. Interception around advice

          Spring中最基本的通知類型是interception around advice .

          Spring使用方法攔截器的around通知是和AOP聯盟接口兼容的。實現around通知的 類需要實現接口MethodInterceptor

          public interface MethodInterceptor extends Interceptor {

           

              Object invoke(MethodInvocation invocation) throws Throwable;

          }

          invoke()方法的MethodInvocation 參數暴露將被調用的方法、目標連接點、AOP代理和傳遞給被調用方法的參數。 invoke()方法應該返回調用的結果:連接點的返回值。

          一個簡單的MethodInterceptor實現看起來如下:

          public class DebugInterceptor implements MethodInterceptor {

           

              public Object invoke(MethodInvocation invocation) throws Throwable {

                  System.out.println("Before: invocation=[" + invocation + "]");

                  Object rval = invocation.proceed();

                  System.out.println("Invocation returned");

                  return rval;

              }

          }

          注意MethodInvocationproceed()方法的調用。這個調用會應用到目標連接點的攔截器鏈中的每一個攔截器。大部分攔截器會調用這個方法,并返回它的返回值。但是, 一個MethodInterceptor,和任何around通知一樣,可以返回不同的值或者拋出一個異常,而 不調用proceed方法。但是,沒有好的原因你要這么做。

          MethodInterceptor提供了和其他AOP聯盟的兼容實現的交互能力。這一節下面 要討論的其他的通知類型實現了AOP公共的概念,但是以Spring特定的方式。雖然使用特定 通知類型有很多優點,但如果你可能需要在其他的AOP框架中使用,請堅持使用MethodInterceptor around通知類型。注意目前切入點不能和其它框架交互操作,并且AOP聯盟目前也沒有定義切入 點接口。

          1.2.2. Before通知

          Before通知是一種簡單的通知類型。 這個通知不需要一個MethodInvocation對象,因為它只在進入一個方法前被調用。

          Before通知的主要優點是它不需要調用proceed() 方法, 因此沒有無意中忘掉繼續執行攔截器鏈的可能性。

          MethodBeforeAdvice接口如下所示。 (SpringAPI設計允許成員變量的before通知,雖然一般的對象都可以應用成員變量攔截,但Spring 有可能永遠不會實現它)。

          public interface MethodBeforeAdvice extends BeforeAdvice {

           

              void before(Method m, Object[] args, Object target) throws Throwable;

          }

          注意返回類型是void Before通知可以在連接點執行之前 插入自定義的行為,但是不能改變返回值。如果一個before通知拋出一個異常,這將中斷攔截器 鏈的進一步執行。這個異常將沿著攔截器鏈后退著向上傳播。如果這個異常是unchecked的,或者 出現在被調用的方法的簽名中,它將會被直接傳遞給客戶代碼;否則,它將被AOP代理包裝到一個unchecked 的異常里。

          下面是Spring中一個before通知的例子,這個例子計數所有正常返回的方法:

          public class CountingBeforeAdvice implements MethodBeforeAdvice {

              private int count;

              public void before(Method m, Object[] args, Object target) throws Throwable {

                  ++count;

              }

           

              public int getCount() {

                  return count;

              }

          }

          Before通知可以被用于任何類型的切入點。

          1.2.3. Throws通知

          如果連接點拋出異常,Throws通知 在連接點返回后被調用。Spring提供強類型的throws通知。注意這意味著 org.springframework.aop.ThrowsAdvice接口不包含任何方法: 它是一個標記接口,標識給定的對象實現了一個或多個強類型的throws通知方法。這些方法形式 如下:

          afterThrowing([Method], [args], [target], subclassOfThrowable)

          只有最后一個參數是必需的。這樣從一個參數到四個參數,依賴于通知是否對方法和方法 的參數感興趣。下面是throws通知的例子。

          如果拋出RemoteException異常(包括子類), 這個通知會被調用

          public class RemoteThrowsAdvice implements ThrowsAdvice {

           

              public void afterThrowing(RemoteException ex) throws Throwable {

                  // Do something with remote exception

              }

          }

          如果拋出ServletException異常, 下面的通知會被調用。和上面的通知不一樣,它聲明了四個參數,所以它可以訪問被調用的方法,方法的參數和目標對象:

          public static class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

           

              public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {

                  // Do something will all arguments

              }

          }

          最后一個例子演示了如何在一個類中使用兩個方法來同時處理 RemoteExceptionServletException 異常。任意個數的throws方法可以被組合在一個類中。

          public static class CombinedThrowsAdvice implements ThrowsAdvice {

           

              public void afterThrowing(RemoteException ex) throws Throwable {

                  // Do something with remote exception

              }

           

              public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {

                  // Do something will all arguments

              }

          }

          Throws通知可被用于任何類型的切入點。

          1.2.4. After Returning通知

          Spring中的after returning通知必須實現 org.springframework.aop.AfterReturningAdvice 接口,如下所示:

          public interface AfterReturningAdvice extends Advice {

           

              void afterReturning(Object returnValue, Method m, Object[] args, Object target)

                      throws Throwable;

          }

          After returning通知可以訪問返回值(不能改變)、被調用的方法、方法的參數和目標對象。

          下面的after returning通知統計所有成功的沒有拋出異常的方法調用:

          public class CountingAfterReturningAdvice implements AfterReturningAdvice {

              private int count;

           

              public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {

                  ++count;

              }

           

              public int getCount() {

                  return count;

              }

          }

          這方法不改變執行路徑。如果它拋出一個異常,這個異常而不是返回值將被沿著攔截器鏈向上拋出。

          After returning通知可被用于任何類型的切入點。

          1.2.5. Introduction通知

          Springintroduction通知看作一種特殊類型的攔截通知。

          Introduction需要實現IntroductionAdvisor, IntroductionInterceptor接口:

          public interface IntroductionInterceptor extends MethodInterceptor {

           

              boolean implementsInterface(Class intf);

          }

          繼承自AOP聯盟MethodInterceptor接口的 invoke()方法必須實現導入:也就是說,如果被調用的方法是在 導入的接口中,導入攔截器負責處理這個方法調用,它不能調用proceed() 方法。

          Introduction通知不能被用于任何切入點,因為它只能作用于類層次上,而不是方法。你可以只用InterceptionIntroductionAdvisor來實現導入通知,它有下面的方法:

          public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {

           

              ClassFilter getClassFilter();

           

              IntroductionInterceptor getIntroductionInterceptor();

           

              Class[] getInterfaces();

          }

          這里沒有MethodMatcher,因此也沒有和導入通知關聯的 切入點。只有類過濾是合乎邏輯的。

          getInterfaces()方法返回advisor導入的接口。

          讓我們看看一個來自Spring測試套件中的簡單例子。我們假設想要導入下面的接口到一個 或者多個對象中:

          public interface Lockable {

              void lock();

              void unlock();

              boolean locked();

          }

          這個例子演示了一個mixin。我們想要能夠 將被通知對象類型轉換為Lockable,不管它們的類型,并且調用lockunlock方法。如果我們調用 lock()方法,我們希望所有setter方法拋出LockedException異常。這樣我們能添加一個方面使的對象不可變,而它們不需要知道這一點:這是一個很好的AOP例 子。

          首先,我們需要一個做大量轉化的IntroductionInterceptor。 在這里,我們繼承 org.springframework.aop.support.DelegatingIntroductionInterceptor 實用類。我們可以直接實現IntroductionInterceptor接口,但是大多數情況下 DelegatingIntroductionInterceptor是最合適的。

          DelegatingIntroductionInterceptor的設計是將導入 委托到真正實現導入接口的接口,隱藏完成這些工作的攔截器。委托可以使用構造方法參數 設置到任何對象中;默認的委托就是自己(當無參數的構造方法被使用時)。這樣在下面的例子里,委托是DelegatingIntroductionInterceptor的子類 LockMixin。給定一個委托(默認是自身)的 DelegatingIntroductionInterceptor實例尋找被這個委托(而不 是IntroductionInterceptor)實現的所有接口,并支持它們中任何一個導入。子類如 LockMixin也可能調用suppressInterflace(Class intf) 方法隱藏不應暴露的接口。然而,不管IntroductionInterceptor 準備支持多少接口,IntroductionAdvisor將控制哪個接口將被實際 暴露。一個導入的接口將隱藏目標的同一個接口的所有實現。

          這樣,LockMixin繼承DelegatingIntroductionInterceptor 并自己實現Lockable。父類自動選擇支持導入的Lockable,所以我們不需要指定它。用這種方法我們可以導入任意數量的接口。

          注意locked實例變量的使用。這有效地添加額外的狀態到目標 對象。

          public class LockMixin extends DelegatingIntroductionInterceptor

              implements Lockable {

           

              private boolean locked;

           

              public void lock() {

                  this.locked = true;

              }

           

              public void unlock() {

                  this.locked = false;

              }

           

              public boolean locked() {

                  return this.locked;

              }

           

              public Object invoke(MethodInvocation invocation) throws Throwable {

                  if (locked() && invocation.getMethod().getName().indexOf("set") == 0)

                      throw new LockedException();

                  return super.invoke(invocation);

              }

           

          }

          通常不要需要改寫invoke()方法:實現 DelegatingIntroductionInterceptor就足夠了,如果是導入的方法, DelegatingIntroductionInterceptor實現會調用委托方法, 否則繼續沿著連接點處理。在現在的情況下,我們需要添加一個檢查:在上鎖狀態下不能調用setter方法。

          所需的導入advisor是很簡單的。只有保存一個獨立的 LockMixin實例,并指定導入的接口,在這里就是 Lockable。一個稍微復雜一點例子可能需要一個導入攔截器(可以 定義成prototype)的引用:在這種情況下,LockMixin沒有相關配置,所以我們簡單地 使用new來創建它。

          public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

           

              public LockMixinAdvisor() {

                  super(new LockMixin(), Lockable.class);

              }

          }

          我們可以非常簡單地使用這個advisor:它不需要任何配置。(但是,有一點 必要的:就是不可能在沒有IntroductionAdvisor 的情況下使用IntroductionInterceptor。) 和導入一樣,通常 advisor必須是針對每個實例的,并且是有狀態的。我們會有不同的的LockMixinAdvisor 每個被通知對象,會有不同的LockMixin advisor組成了被通知對象的狀態的一部分。

          和其他advisor一樣,我們可以使用 Advised.addAdvisor() 方法以編程地方式使用這種advisor,或者在XML中配置(推薦這種方式)。 下面將討論所有代理創建,包括自動代理創建者,選擇代理創建以正確地處理導入和有狀態的混入。

           

           

          參考資料:

          1. http://www.javaresearch.org/article/showarticle.jsp?column=23&thread=41315

          2. http://tech.ccidnet.com/art/1112/20051114/371959_5.html

          3. http://www.7dspace.com/doc/21/0603/20063305365394884.htm

          4. http://barton131420.cnblogs.com/articles/280664.html

          5. http://www.opentown.info/bbs/viewtopic.php?t=7

           

          posted on 2007-06-28 09:36 junky 閱讀(714) 評論(0)  編輯  收藏 所屬分類: spring

          主站蜘蛛池模板: 泰兴市| 宁乡县| 齐河县| 洪泽县| 乐山市| 称多县| 黑龙江省| 汕尾市| 鸡东县| 尚义县| 密云县| 伊金霍洛旗| 鄂伦春自治旗| 通州区| 湄潭县| 和林格尔县| 红桥区| 贵州省| 高清| 沙雅县| 济宁市| 曲阳县| 奉贤区| 海口市| 兴隆县| 长治县| 福鼎市| 嘉禾县| 湛江市| 古蔺县| 三台县| 阳春市| 澄迈县| 双牌县| 长垣县| 句容市| 高碑店市| 威宁| 遂宁市| 瑞安市| 静宁县|