邊城愚人

          如果我不在邊城,我一定是在前往邊城的路上。

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            31 隨筆 :: 0 文章 :: 96 評(píng)論 :: 0 Trackbacks

          ??? ??? 讓我好好想想, AspectJ 中最常用的切入點(diǎn)是什么?哦,也許是 call Method-Signature )吧。這是個(gè)相對(duì)簡(jiǎn)單的方法簽名。實(shí)際上,方法簽名的完整形式如下:

          [modifiers] [returnTypePattern] [DeclaredTypePattern.]methodName([Parameters])[throws TypePattern] ,其中方括號(hào)中的簽名組件是可選的。 modifiers 為修飾符模式, returnTypePattern 為返回類型模式, DeclaredTypePattern 為類型聲明模式, methodName 為方法名稱, Parameters 為方法參數(shù), throws TypePattern throw 字句。該文僅僅介紹 DeclaredTypePattern ,因?yàn)橄啾戎缕渌J奖容^簡(jiǎn)單的多。

          ??? ??? 在介紹類型聲明模式之前,介紹一下類型模式。類型模式是匹配一種類型或一系列類型的方法。精確的類型模式是如 java.lang.String 一樣的完整的有效類型名。但在使用 AspectJ 類型模式時(shí),經(jīng)常會(huì)用到下列通配符(這些通配符同樣適用于 Spring AOP )。

          1 )“ *” :代表任意字符的零次或多次出現(xiàn)。當(dāng)嵌入到一串字符的內(nèi)部時(shí),它匹配任意字符的零次或多次出現(xiàn),除了包分割符( . )。

          2 )“ +” :用作類型模式的后綴,代表此類型和其所有的子類型(那些擴(kuò)展或?qū)崿F(xiàn)帶后綴類型的類型)。

          3 )“ ..” :用于指定所有的子包,它匹配任意以包分割符開頭和結(jié)束的字符串。
          ?

          下面給出幾個(gè)示例:

          1 *Account 使用 Account 名稱結(jié)束的類型,如 CheckingAccount
          2
          java.*.Date 類型 Date 在任何直接的 java 子包中,如 java.util.Date java.sql.Date
          3
          java..* 任何在 java 包或者所有子包中的類型,如 java.awt 或者 java.awt.event
          4
          javax..*Model+ 所有 javax 包或者子包中以 Model 結(jié)尾的類型和其所有子類,如 TableModel,TreeModel 。

          ??? ??? 現(xiàn)在開始說(shuō)說(shuō)類型聲明模式。實(shí)際上,在方法簽名中,類型聲明模式不是必需的(就像很多書中所說(shuō),應(yīng)該少用類型聲明模式而改用與“ target” 結(jié)合的切入點(diǎn)指示符)。但如果指定了類型聲明模式,切入點(diǎn)將只匹配對(duì)由模式匹配的類型(或者超類型)聲明的方法的調(diào)用。和其他類型模式一樣,類型聲明模式支持上述的通配符。同時(shí),它也支持復(fù)合類型模式。對(duì)于類型聲明模式來(lái)說(shuō),程序員容易犯錯(cuò)的地方在于類型聲明模式是基于靜態(tài)類型而不是運(yùn)行時(shí)類型,這也是本文的主要內(nèi)容。

          ? ??

          在很好地理解類型聲明模式之前,先看一下下面的例子:

          public ? class ?A??{
          ????
          public ? void ?foo(){
          ????????System.out.println(
          " A.foo() " );
          ????}
          }
          public ? class ?B? extends ?A{
          ????
          public ? void ?foo(){
          ????????System.out.println(
          " B.foo() " );
          ????}
          }
          public ? class ?Main?{

          ????
          ????
          public ? static ? void ?main(String[]?args)?{
          ????????A?b?
          = ? new ?B();
          ????????b.foo();?
          // (1)
          ????????callFoo(b); // (2)
          ????}

          ????
          public ? static ? void ?callFoo(A?a){
          ????????System.out.println(
          " Call?A " );
          ????}
          ????
          ????
          public ? static ? void ?callFoo(B?b){
          ????????System.out.println(
          " Call?B " );
          ????}
          }

          ??? ??? 它的運(yùn)行結(jié)果是這樣的:
          ??? ??? B.foo()

          ??? ??? Call A

          ??? ??? 和你的想法一致嗎?對(duì)于( 1 )處 b.foo() 的調(diào)用應(yīng)用了面向?qū)ο笾械母采w( override ),它是動(dòng)態(tài)的,是在運(yùn)行時(shí)進(jìn)行解析。而( 2 )處的 callFoo (b) 則是重載( overload ),它是靜態(tài)的,是在編譯時(shí)解析的。因此,對(duì)于變量 b ,雖然它是 B 的一個(gè)實(shí)例,但 b 的靜態(tài)類型(也就是變量聲明的類型)是 A ;由于重載方法的選擇是靜態(tài)的,所以 main 中調(diào)用的是 callFoo(A a) ,而不是 callFoo(B b)

          ??? ??? 終于說(shuō)到了類型聲明模式。類型聲明模式是基于靜態(tài)類型信息進(jìn)行匹配的,而不是動(dòng)態(tài)(或者運(yùn)行時(shí)。下面根據(jù)幾個(gè)典型的例子說(shuō)明類型聲明模式的特性。

          ??? ??? 還是上面的兩個(gè)類 A B ,現(xiàn)在我們定義一個(gè)方面如下:


          public ?aspect?TypeAspect?{

          ????????pointcut?callA():
          ????????????call(
          * ?A. * (..));
          ????????
          ????????before():callA(){
          ????????????System.out.println(
          " call?A " );
          ????????}????
          }

          ??? ??? main 函數(shù)內(nèi)容如下:


          public ? static ? void ?main(String[]?args)?{
          ????????A?b1?
          = ? new ?B();
          ????????b1.foo();
          ????????
          ????????B?b2?
          = ? new ?B();
          ????????b2.foo();
          ????}

          ??? ??

          運(yùn)行結(jié)果如下:

          call A

          B.foo()

          call A

          B.foo()

          ??? ??? 可以看到,盡管切入點(diǎn) callA() 聲明的類型為 A ,但實(shí)際上,切入點(diǎn) callA() 可以捕獲 A 中的方法及其子類中繼承于 A 的方法或重載 A 的方法,而聲明的靜態(tài)類型既可以是 A 也可以是其子類。

          ??? ??? 但如果在 B 中增加一個(gè)新的方法:

          public ? void ?doAnotherThing(){

          System.out.println(
          " B.doAnotherThing " );

          }

          ??? ??? main 函數(shù)改為:

          public ? static ? void ?main(String[]?args)?{

          B?b2?
          = ? new ?B();

          b2.doAnotherThing();

          }

          ?? ??? 輸出結(jié)果為: B.doAnotherThing ,如果想對(duì) A 的子類 B 中擴(kuò)展的方法進(jìn)行通知,可采用的方法是將切入點(diǎn) callA() 改為 pointcut callA(): call (* A+.*(..));

          ??? ??? 讓我們?cè)賮?lái)看另一種情景:如果定義一個(gè)切入點(diǎn)如下:


          ?pointcut?callB():call( * ?B. * (..));

          before():callB(){

          System.out.println(
          " call?B " );

          }

          ??? ??? main 函數(shù)內(nèi)容如下:


          public ? static ? void ?main(String[]?args)?{

          A?b?
          = ? new ?B();

          b.foo();

          }

          ??? ??? 運(yùn)行結(jié)果為: B.foo() 。 b.foo() 沒有匹配切入點(diǎn) callB() 的原因在于, b 的靜態(tài)類型是 A ,從靜態(tài)類型的角度來(lái)看,這是對(duì) A 的調(diào)用,而不是對(duì) B 的調(diào)用。在使用 AspectJ 的類型聲明時(shí),很容易在這個(gè)地方犯錯(cuò)。

          ??? ??? 好了,如上便是有關(guān)類型聲明模式的東西,說(shuō)得有些凌亂,希望對(duì) AspectJ 初學(xué)者有些幫助(我本身也是個(gè)初學(xué)者)。該文參考了《 Eclipse AspectJ 》和《 AspectJ cookbook 》。


          posted on 2007-07-07 14:54 kafka0102 閱讀(1750) 評(píng)論(2)  編輯  收藏 所屬分類: AOP

          評(píng)論

          # re: AspectJ學(xué)習(xí)(1)理解方法簽名中的類型聲明模式 2007-07-08 22:37 go
          good...  回復(fù)  更多評(píng)論
            

          # re: AspectJ學(xué)習(xí)(1)理解方法簽名中的類型聲明模式[未登錄] 2013-01-08 11:21 cj
          good,頂頂  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 射洪县| 四会市| 西城区| 加查县| 遵化市| 桂东县| 广东省| 绥棱县| 肇东市| 彰化市| 大同市| 九寨沟县| 万宁市| 甘南县| 察哈| 田东县| 平乡县| 古丈县| 伊宁县| 莱阳市| 那曲县| 衡水市| 南陵县| 比如县| 澎湖县| 鄂托克前旗| 西乌珠穆沁旗| 楚雄市| 锦屏县| 丽江市| 甘泉县| 两当县| 南投市| 隆德县| 锦屏县| 麻城市| 神池县| 湟中县| 巩义市| 凤城市| 锦屏县|