Spring AOP @AspectJ 入門基礎

須要的類包:java

 

一、一個簡單的例子spring

Waiter接口:
package com.yyq.annotation;
public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}

NaiveWaiter業務類:數組

package com.yyq.annotation;
public class NaiveWaiter implements Waiter {
    @Override
    public void greetTo(String name) {
        System.out.println("NaiveWaiter:greet to " + name + "...");
    }
    @Override
    public void serveTo(String name) {
        System.out.println("NaiveWaiter:serving to " + name + "...");
    }
}

PreGreetingAspect切面實現類:ide

package com.yyq.annotation;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//經過該註解將PreGreetingAspect標識爲一個切面
@Aspect
public class PreGreetingAspect {
    //定義切點和加強類型
    @Before("execution(* greetTo(..))")
    //加強的橫切邏輯
    public void beforeGreeting(){
        System.out.println("How are you");
    }
}

測試方法:函數

@Test
    public void aspectJProxyTest(){
        Waiter target = new NaiveWaiter();
        AspectJProxyFactory factory = new AspectJProxyFactory();
        //設置目標對象
        factory.setTarget(target);
        //添加切面對象
        factory.addAspect(PreGreetingAspect.class);
        //生成織入切面的代理對象
        Waiter proxy = factory.getProxy();
        proxy.greetTo("Anny");
        proxy.serveTo("Mei");
    }

輸出結果:測試

How are you
NaiveWaiter:greet to Anny...
NaiveWaiter:serving to Mei...
 
經過Spring配置使用@AspectJ切面:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!--目標Bean-->
    <bean id="waiter" class="com.yyq.annotation.NaiveWaiter"/>
    <!--使用了@AspectJ註解的切面類-->
    <bean class="com.yyq.annotation.PreGreetingAspect"/>
    <!--自動代理建立器,自動將@AspectJ註解切面類織入到目標Bean中-->
    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
</beans>

測試方法:this

@Test
    public void aspectJProxyTest2(){
        String configPath = "com\\yyq\\annotation\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter");
        waiter.greetTo("John");
    }
輸出結果:
How are you
NaiveWaiter:greet to John...
 
使用基於Schema的AOP命名空間配置:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <aop:aspectj-autoproxy/>
    <bean id="waiter" class="com.yyq.annotation.NaiveWaiter"/>
    <bean class="com.yyq.annotation.PreGreetingAspect"/>
</beans>

 測試方法:spa

  @Test
    public void aspectJProxyTest3(){
        String configPath = "com\\yyq\\annotation\\beans2.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter");
        waiter.greetTo("Herry");
    }
輸出結果:
How are you
NaiveWaiter:greet to Herry...
 
二、@AspectJ基礎
    1)切點表達式函數
    AspectJ 5的切點表達式由關鍵字和操做參數組成,如execution(* greetTo(..))的切點表達式,「execute」爲關鍵字,而「* greetTo(..)」爲操做參數。在這裏,execute表明目標類執行某一方法,而「* greetTo(..)」是描述目標方法的匹配模式串,二者聯合起來所表示的切點匹配目標類greetTo()方法的鏈接點。爲了描述方便,咱們將 execution()稱做函數,而將匹配串「* greetTo(..)」稱做函數的入參。 

    Spring支持9個@ApsectJ切點表達式函數,它們用不一樣的方式描述目標類的鏈接點,根據描述對象的不一樣,能夠將它們大體分爲4種類型: 
        · 方法切點函數:經過描述目標類方法信息定義鏈接點; 
        · 方法入參切點函數:經過描述目標類方法入參的信息定義鏈接點; 
        · 目標類切點函數:經過描述目標類類型信息定義鏈接點; 
        · 代理類切點函數:經過描述目標類的代理類的信息定義鏈接點; 代理

類別 函數 入參 說明
方法切點函數 execution() 方法匹配模式串 表示知足某一匹配模式的全部目標類方法鏈接點。如execution(* greetTo(..))表示全部目標類中的greetTo()方法。
@annotation() 方法註解類名 表示標註了特定註解的目標方法鏈接點。如@annotation(com.baobaotao.anno.NeedTest)表示任何標註了@NeedTest註解的目標類方法。
方法入參切點函數 args() 類名 經過判別目標類方法運行時入參對象的類型定義指定鏈接點。如args(com.baobaotao.Waiter)表示全部有且僅有一個按類型匹配於Waiter的入參的方法。
@args() 類型註解類名 經過判別目標方法的運行時入參對象的類是否標註特定註解來指定鏈接點。如@args(com.baobaotao.Monitorable)表示任何這樣的一個目標方法:它有一個入參且入參對象的類標註@Monitorable註解。
目標類切點函數 within() 類名匹配串 表示特定域下的全部鏈接點。如within(com.baobaotao.service.*)表示 com.baobaotao.service包中的全部鏈接點,也即包中全部類的全部方法,而 within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中,全部以 Service結尾的類的全部鏈接點。
target() 類名 假如目標類按類型匹配於指定類,則目標類的全部鏈接點匹配這個切點。如經過target(com.baobaotao.Waiter)定義的切點,Waiter、以及Waiter實現類NaiveWaiter中全部鏈接點都匹配該切點。
@within() 類型註解類名 假如目標類按類型匹配於某個類A,且類A標註了特定註解,則目標類的全部鏈接點匹配這個切點。如@within(com.baobaotao.Monitorable)定義的切點,假如Waiter類標註了@Monitorable註解,則Waiter以及Waiter實現類NaiveWaiter類的全部鏈接點都匹配。
@target() 類型註解類名 目標類標註了特定註解,則目標類全部鏈接點匹配該切點。如@target(com.baobaotao.Monitorable),假如NaiveWaiter標註了@Monitorable,則NaiveWaiter全部鏈接點匹配切點。
代理類切點函數 this() 類名 代理類按類型匹配於指定類,則被代理的目標類全部鏈接點匹配切點。

 

    2)在函數入參中使用通配符
    @AspectJ支持3種通配符:
  •  * 匹配任意字符,但它只能匹配上下文中的一個元素;   
  •  .. 匹配任意字符,能夠匹配上下文中的多個元素,但在表示時,必須和 * 聯合使用,而在表示入參時則單獨使用;
  •  + 表示按類型匹配指定類的全部類,必須跟在類名後面,如com.yyq.Car+。繼承或擴展指定類的全部類,同時還包括指定類自己。
    @AspectJ函數按其是否支持通配符及支持的程度,能夠分爲如下3類:
  • 支持全部通配符:execution()、within(),如within(com.yyq.*)、within(com.yyq.service..*.*Service+)等;
  • 僅支持 + 通配符:args()、this()、target(),如args(com.yyq.Waiter+)、target(java.util.List+)等。雖然這3個函數能夠支持+通配符,但其意義不大,由於對於這些函數來講使用和不使用+都是同樣的,如target(com.yyq.Waiter+)和target(com.yyq.Waiter)是等價的。
  • 不支持通配符:@args()、@within()、@target()和@annotation(),如@args(com.yyq.anno.NeedTest)和@within(com.yyq.anno.NeedTest)。
    此外,args()、this()、target()、@args()、@within()、@target()和@annotation()這7個函數除了能夠指定類名外,也能夠指定變量名,並將目標對象中的變量綁定到加強的方法中。
 
    3)邏輯運算符
    切點表達式由切點函數組成,切點函數之間還能夠進行邏輯運算,組成複合切點,Spring支持如下的切點運算符:
  • && 與操做符,至關於切點的交集運算,and是等效的操做符。如within(com.yyq..*) and args(String) 表示在com.yyq包下全部類(當前包以及子孫包)擁有一個String入參的方法;
  • ||  或操做符,至關於切點的並集運算,or是等效的操做符。如within(com.yyq..*) || args(String) 表示在com.yyq包下的全部類的方法,或者全部擁有一個String入參的方法;
  • ! 非操做符,至關於切點的反集運算,not是等效的操做符。如!within(com.yyq.*) 表示全部不在com.yyq包下的方法。
 
    4)不一樣加強類型
    @AspectJ爲各類的加強類型提供了不一樣的註解類,它們位於org.aspectj.lang.annotation.*包中,這些註解類擁有若干個成員,能夠經過這些成員完成定義切點信息、綁定鏈接點參數等操做;此外,這些註解的存留期限都是RetentionPolicy.RUNTIME,標註目標都是ElementType.METHOD。
  • @Before
        前置加強,至關於BeforeAdvice的功能,Before註解類擁有兩個成員:
    • value:該成員用於定義切點;
    • argNames:因爲沒法經過Java反射機制獲取方法入參名,全部若是在Java編譯時未啓動調試信息或者須要在運行期解析切點,就必須經過這個成員指定註解所標註加強方法的參數名(注意二者名字必須徹底相同),多個參數名用逗號分隔。
  • AfterReturning
        後置加強,至關於AfterReturningAdvice,AfterReturning註解類擁有4個成員:
    • value:該成員用於定義切點;
    • pointcut:表示切點的信息,若是顯式指定pointcut值,它將覆蓋value的設置值,能夠將pointcut成員當作是value的同義詞;
    • returning:將目標對象方法的返回值綁定給加強的方法;
    • argNames:如前所述。
  • Around
        環繞加強,至關於MethodInterceptor,Around註解類擁有兩個成員:
    • value:該成員用於定義切點;
    • argNames:如前所述。
  • AfterThrowing
        拋出加強,至關於ThrowsAdvice,AfterThrowing註解類擁有4個成員:
    • value:該成員用於定義切點;
    • pointcut:表示切點的信息,若是顯式指定pointcut值,它將覆蓋value的設置值,能夠將pointcut成員當作是value的同義詞;
    • throwing:將拋出的異常綁定到加強方法中;
    • argNames:如前所述。
  • After
        Final加強,不論是拋出異常或者是正常退出,該加強都會獲得執行,該加強沒有對應的加強接口,能夠把它當作是ThrowsAdvice和AfterReturningAdvice的混合物,通常用於釋放資源,至關於try{}finally{}的控制流。After註解類擁有兩個成員:
    • value:該成員用於定義切點;
    • argNames:如前所述。
  • DeclareParents
        引介加強,至關於IntroductionInterceptor,DeclareParents註解類擁有兩個成員:
    • value:該成員用於定義切點,它表示在哪一個目標類上添加引介加強;
    • defaultImpl:默認的接口實現類。
     5)引介加強用法
    假設但願NaiveWaiter可以同時充當售貨員的角色,即經過切面技術爲NaiveWaiter新增Seller接口的實現。
Seller接口:
package com.yyq.annotation;
public interface Seller {
  int sell(String goods, String clientName);
}

SmartSeller實現類:調試

package com.yyq.annotation;
public class SmartSeller implements Seller {
    public int sell(String goods,String clientName) {
        System.out.println("SmartSeller: sell "+goods +" to "+clientName+"...");
        return 100;
    }
}

EnableSellerAspect切面實現類:

package com.yyq.annotation;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
@Aspect
public class EnableSellerAspect {
    @DeclareParents(value = "com.yyq.annotation.NaiveWaiter",defaultImpl = SmartSeller.class)
    public Seller seller;
}

beans3.xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <aop:aspectj-autoproxy/>
    <bean id="waiter" class="com.yyq.annotation.NaiveWaiter"/>
    <bean class="com.yyq.annotation.EnableSellerAspect"/>
</beans>

測試方法:

@Test
    public void aspectJProxyTest4(){
        String configPath = "com\\yyq\\annotation\\beans3.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter waiter = (Waiter)ctx.getBean("waiter");
        waiter.greetTo("John");
        Seller seller = (Seller)waiter;   //成功進行強制類型轉換
        seller.sell("Beer","John");
    }
輸出結果:
NaiveWaiter:greet to John...
SmartSeller: sell Beer to John...
 
三、切點函數詳解
    1)@annotation()
    @annotation() 表示標註了某個註解的全部方法。
NeedTest標註類:
package com.yyq.aspectJ;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NeedTest {
    boolean value() default true;
}

 Waiter接口:

package com.yyq.aspectJ;
public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}

NaiveWaiter實現類:

package com.yyq.aspectJ;
public class NaiveWaiter implements Waiter {
    @Override
    public void greetTo(String name) {
        System.out.println("NaiveWaiter:greet to " + name + "...");
    }
    @Override
    public void serveTo(String name) {
        System.out.println("NaiveWaiter:serving to " + name + "...");
    }
}

NaughtyWaiter實現類:

package com.yyq.aspectJ;
public class NaughtyWaiter implements Waiter {
    @NeedTest
    public void greetTo(String clientName) {
        System.out.println("NaughtyWaiter:greet to "+clientName+"...");
    }    
    public void serveTo(String clientName){
        System.out.println("NaughtyWaiter:serving "+clientName+"...");
    }
    public void joke(String clientName,int times){
            System.out.println("NaughtyWaiter:play "+times+" jokes to "+clientName+"...");
    }
}

TestAspcet切面實現類:

package com.yyq.aspectJ;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TestAspect {
    @AfterReturning("@annotation(com.yyq.aspectJ.NeedTest)")
    public void needTestFun(){
        System.out.println("needTestFun() executed!");
    }
}

Spring配置自動應用切面:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <aop:aspectj-autoproxy/>
    <bean id="naiveWaiter" class="com.yyq.aspectJ.NaiveWaiter"/>
    <bean id="naughtyWaiter" class="com.yyq.aspectJ.NaughtyWaiter"/>
    <bean class="com.yyq.aspectJ.TestAspect"/>
</beans>

測試方法:

package com.yyq;
import com.yyq.aspectJ.Waiter;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AspectJTest {
    @Test
    public void annotationAspectJTest() {
        String configPath = "com\\yyq\\aspectJ\\beans.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
        Waiter naiveWaiter = (Waiter) ctx.getBean("naiveWaiter");
        Waiter naughtyWaiter = (Waiter) ctx.getBean("naughtyWaiter");
        naiveWaiter.greetTo("John");
        naiveWaiter.serveTo("John");
        naughtyWaiter.greetTo("Tom");
        naughtyWaiter.serveTo("Tom");
    }
}
輸出結果:
NaiveWaiter:greet to John...
NaiveWaiter:serving to John...
NaughtyWaiter:greet to Tom...
needTestFun() executed!
NaughtyWaiter:serving Tom...
 
    2)execution()
        execution()是最經常使用的切點函數,其語法以下所示:
        execution(<修飾符模式>? <返回類型模式> <方法名模式>(<參數模式>) <異常模式>?)
         除了返回類型模式、方法名模式和參數模式外,其它項都是可選的。
  • 經過方法簽名定義切點  
            · execution(public * *(..)) 
                匹配全部目標類的public方法,但不匹配SmartSeller和protected void showGoods()方法。第一個*表明返回類型,第二個*表明方法名,而..表明任意入參的方法;         
            · execution(* *To(..)) 
                匹配目標類全部以To爲後綴的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一個*表明返回類型,而*To表明任意以To爲後綴的方法; 
  • 經過類定義切點 
             · execution(* com.baobaotao.Waiter.*(..)) 
                匹配Waiter接口的全部方法,它匹配NaiveWaiter和NaughtyWaiter類的greetTo()和serveTo()方法。第一個*表明返回任意類型,com.baobaotao.Waiter.*表明Waiter接口中的全部方法; 
            · execution(* com.baobaotao.Waiter+.*(..)) 
                匹配Waiter接口及其全部實現類的方法,它不但匹配NaiveWaiter和NaughtyWaiter類的greetTo()和serveTo()這兩個Waiter接口定義的方法,同時還匹配NaiveWaiter#smile()和NaughtyWaiter#joke()這兩個不在Waiter 接口中定義的方法。
  • 經過類包定義切點
        在類名模式串中,「.*」表示包下的全部類,而「..*」表示包、子孫包下的全部類。 
            · execution(* com.baobaotao.*(..)) 
                匹配com.baobaotao包下全部類的全部方法; 
            · execution(* com.baobaotao..*(..)) 
                匹配com.baobaotao包、子孫包下全部類的全部方法,如com.baobaotao.dao,com.baobaotao.servier以及 com.baobaotao.dao.user包下的全部類的全部方法都匹配。「..」出如今類名中時,後面必須跟「*」,表示包、子孫包下的全部類; 
            · execution(* com..*.*Dao.find*(..)) 
                匹配包名前綴爲com的任何包下類名後綴爲Dao的方法,方法名必須以find爲前綴。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切點。 
  • 經過方法入參定義切點 
        切點表達式中方法入參部分比較複雜,可使用「*」和「 ..」通配符,其中「*」表示任意類型的參數,而「..」表示任意類型參數且參數個數不限。 
            · execution(* joke(String,int))) 
                匹配joke(String,int)方法,且joke()方法的第一個入參是String,第二個入參是int。它匹配 NaughtyWaiter#joke(String,int)方法。若是方法中的入參類型是java.lang包下的類,能夠直接使用類名,不然必須使用全限定類名,如joke(java.util.List,int); 
            · execution(* joke(String,*))) 
                匹配目標類中的joke()方法,該方法第一個入參爲String,第二個入參能夠是任意類型,如joke(String s1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,double d2,String s3)則不匹配;
            · execution(* joke(String,..))) 
                匹配目標類中的joke()方法,該方法第 一個入參爲String,後面能夠有任意個入參且入參類型不限,如joke(String s1)、joke(String s1,String s2)和joke(String s1,double d2,String s3)都匹配;
            · execution(* joke(Object+))) 
                匹配目標類中的joke()方法,方法擁有一個入參,且入參是Object類型或該類的子類。 它匹配joke(String s1)和joke(Client c)。若是咱們定義的切點是execution(* joke(Object)),則只匹配joke(Object object)而不匹配joke(String cc)或joke(Client c)。
 
    3)args()和@args()
        args()函數的入參是類名,@args()函數的入參必須是註解類的類名。雖然args()容許在類名後使用+通配符後綴,但該通配符在此處沒有意義:添加和不添加效果都同樣。 
  • args()        
        該函數接受一個類名,表示目標類方法入參對象按類型匹配於指定類時,切點匹配。如:args(com.baobaotao.Waiter) 
        表示運行時入參是Waiter類型的方法,它和execution(* *(com.baobaotao.Waiter))區別在於後者是針對 類方法的簽名而言的,而前者則針對 運行時的入參類型而言。如 args(com.baobaotao.Waiter)既匹配於addWaiter(Waiter waiter),也匹配於addNaiveWaiter(NaiveWaiter naiveWaiter),而execution(* *(com.baobaotao.Waiter))只匹配addWaiter(Waiter waiter)方法。實際上,args(com.baobaotao.Waiter)等價於execution(* *(com.baobaotao.Waiter+)),固然也等價於args(com.baobaotao.Waiter+)。
  • @args()
        該函數接受一個註解類的類名,當方法的運行時入參對象標註髮指定的註解時,方法匹配切點。若是在類繼承樹中註解點@M高於入參類型點fun(T1 t),則該目標方法不可能匹配切點@args(M);若是在類繼承樹中註解點@M低於入參類型點fun(T1 t),則註解點所在類及其子孫類做爲方法入參時,該方法匹配@args(M)切點。
        假設咱們定義這樣的切點:@args(com.baobaotao.Monitorable) ,若是NaiveWaiter標註了@Monitorable,則對於WaiterManager#addWaiter(Waiter w)方法來講,若是入參是NaiveWaiter或其子類對象,該方法匹配切點,若是入參是NaughtyWaiter對象,不匹配切點。若是 Waiter標註了@Monitorable,但NaiveWaiter未標註@Monitorable,則 WaiterManager#addNaiveWaiter(NaiveWaiter w)卻不匹配切點,這是由於註解點(在Waiter)高於入參類型點(NaiveWaiter)。 
 
    4)within()
        經過類匹配模式串聲明切點,within()函數定義的鏈接點是針對 目標類而言,而 非針對運行期對象的類型而言,這一點和execetion()是相同 的。但和execution()函數不一樣的是,within()所指定的鏈接點最小範圍只能是類,而execution()所指定的鏈接點,能夠大到包, 小到方法入參。因此從某種意義上說,execution()函數的功能涵蓋了within()函數的功能。within()函數的語法: within(<類匹配模式>) 

        形如within(com.baobaotao.NaiveWaiter)是within()函數所能表達的最小粒度,若是試圖用within()匹配方法級別的鏈接點,如within(com.baobaotao.NaiveWaiter.greet*)將會產生解析錯誤。  

  • within(com.baobaotao.NaiveWaiter) 

        匹配目標類NaiveWaiter的全部方法。若是切點調整爲within(com.baobaotao.Waiter),則NaiveWaiter和 NaughtyWaiter中的全部方法都不匹配,而Waiter自己是接口不可能實例化,因此 within(com.baobaotao.Waiter)的聲明是無心義的; 

  • within(com.baobaotao.*) 

        匹配com.baobaotao包中的全部類,但不包括子孫包,因此com.baobaotao.service包中類的方法不匹配這個切點; 

  • within(com.baobaotao..*) 

        匹配com.baobaotao包及子孫包中的類,因此com.baobaotao.service、com.baobaotao.dao以及com.baobaotao.service.fourm等包中全部類的方法都匹配這個切點。

 
    5)@within()和@target()
        除 @annotation()和@args()外,還有另外兩個用於註解的切點函數,它們分別是@target()和@within(),和 @annotation()及@args()函數同樣,它們也只接受註解類名做爲入參。其中@target(M)匹配任意標註了@M的目標類,而 @within(M)匹配標註了@M的類及子孫類。 
        假設NaiveWaiter標註了@Monitorable,則其子類CuteNaiveWaiter沒有標註@Monitorable,則 @target(com.baobaotao.Monitorable)匹配NaiveWaiter類的全部方法,但不匹配 CuteNaiveWaiter類的方法。 
        假設NaiveWaiter標註了@Monitorable,而其子類CuteNaiveWaiter沒有標註@Monitorable,則 @within(com.baobaotao.Monitorable)不但匹配NaiveWaiter類中的全部方法也匹配 CuteNaiveWaiter類中的全部方法。
        但有一個特別值得注意地方是,若是標註@M註解的是一個接口,則全部實現該接口的類並不 匹配@within(M)。假設Waiter標註了@Monitorable註解,但NaiveWaiter、NaughtyWaiter及 CuteNaiveWaiter這些接口實現類都沒有標註@Monitorable,則 @within(com.baobaotao.Monitorable)和@target(com.baobaotao.Monitorable)都不匹 配NaiveWaiter、NaughtyWaiter及CuteNaiveWaiter。這是由於@within()、@target()以及 @annotation()都是針對目標類而言,而非針對運行時的引用類型而言,這點區別須要在開發中特別注意。

    6)target()和this()
        target()切點函數經過判斷目標類是否按類型匹配指定類決定鏈接點是否匹配,而this()則經過判斷代理類是否按類型匹配指定類來決定是否和切點 匹配。二者都僅接受類名的入參,雖然類名能夠帶「+」通配符,但對於這兩個函數來講,使用與不使用+通配符,效果徹底相同。
  • target() 
        target(M)表示若是目標類按類型匹配於M,則目標類全部方法匹配切點。
        · target(com.baobaotao.Waiter) 
            NaiveWaiter、NaughtyWaiter以及CuteNaiveWaiter的全部方法都匹配切點,包括那些未在Waiter接口中定義的方法,如NaiveWaiter#simle()和NaughtyWaiter#joke()方法。 
        · target(com.baobaotao.Waiter+) 
            和target(com.baobaotao.Waiter)是等價的。 
  • this()
        根據Spring的官方文檔,this()函數判斷代理對象的類是否按類型匹配於指定類,若是匹配,則代理對象的全部鏈接點匹配切點。但經過實驗,咱們發現實際狀況和文檔有出入,如咱們聲明一個this(com.baobaotao.NaiveWaiter)的切點,若是不使用CGLib代理,則生成的代理對象是Waiter類型,而非NaiveWaiter類型,這一點能夠簡單地經過instanceof操做符進行判斷。可是,咱們發現 NaiveWaiter中全部的方法仍是被織入了加強。 
        在通常狀況下,使用this()和target()經過定義切點,二者是等效的: 
        · target(com.baobaotao.Waiter) 等價於this(com.baobaotao.Waiter) 
        · target(com.baobaotao.NaiveWaiter) 等價於 this(com.baobaotao.NaiveWaiter) 
        二者區別體如今經過引介切面產生的代理對象時的具體表現,若是咱們經過本文前面的方法爲NaiveWaiter引介一個Seller接口的實現,則 this(com.baobaotao.Seller)匹配NaiveWaiter代理對象的全部方法,包括NaiverWaiter自己的 greetTo()、serverTo()方法以及經過Seller接口引入的sell()方法。而 target(com.baobaotao.Seller)不匹配經過引介切面產生的NaiveWaiter代理對象。
相關文章
相關標籤/搜索