Spring AOP基礎(chǔ)

1.AOP概述

1.1.AOP到底是什么

AOP只適合那些具有橫切面邏輯的應(yīng)用場(chǎng)合,如性能監(jiān)測(cè),訪問(wèn)控制品腹,事務(wù)管理及日志記錄

Aop實(shí)例的github地址

1.2.AOP術(shù)語(yǔ)
  • 連接點(diǎn)(JoinPoint):特定點(diǎn)是程序執(zhí)行某個(gè)特定位置,如初始化開始前啸胧,初始化開始后等等赶站。一個(gè)類或一段程序代碼擁有一些具有邊界性質(zhì)的特定點(diǎn),這些代碼中的特定點(diǎn)就被稱為“連接點(diǎn)”纺念。Spring僅支持方法的連接點(diǎn)贝椿,僅能在方法調(diào)用前、方法調(diào)用后陷谱、方法拋出異常時(shí)及方法調(diào)用前后這些程序執(zhí)行點(diǎn)織入增強(qiáng)烙博。
  • 切點(diǎn)(Pointcut):每個(gè)程序類都有多個(gè)連接點(diǎn),就好像每個(gè)類有多個(gè)方法一樣叭首,那么如何定位感興趣的連接點(diǎn)呢?AOP通過(guò)"切點(diǎn)"來(lái)定位特定的連接點(diǎn)习勤。在Spring中,切點(diǎn)通過(guò)Pointcut接口進(jìn)行描述焙格,它使用類和方法作為連接點(diǎn)的查詢條件图毕。
  • 增強(qiáng)(Advice):增強(qiáng)是織入目標(biāo)類連接點(diǎn)上的一段程序代碼,換句話說(shuō)增強(qiáng)就是添加到目標(biāo)連接點(diǎn)上的一段程序邏輯眷唉。并且Spring所提供的增強(qiáng)接口都是帶方位名的予颤,如BeforeAdvice等等。只有結(jié)合切點(diǎn)和增強(qiáng)冬阳,才能確定特定的連接點(diǎn)并實(shí)施增強(qiáng)邏輯蛤虐。
  • 目標(biāo)對(duì)象(Target):增強(qiáng)邏輯織入目標(biāo)類,如果沒有AOP肝陪,那么目標(biāo)業(yè)務(wù)類要自己實(shí)現(xiàn)所有的邏輯驳庭。在AOP的幫助下,程序只需要實(shí)現(xiàn)非橫切邏輯的代碼部分氯窍,而性能監(jiān)視和事務(wù)管理等這些橫切邏輯則可以使用AOP動(dòng)態(tài)織入特定的連接點(diǎn)上饲常。
  • 引介(Introduction):引介是一種特殊的增強(qiáng),它為類添加一些屬性和方法狼讨。這樣贝淤,即使一個(gè)業(yè)務(wù)類原本沒有實(shí)現(xiàn)某個(gè)接口,通過(guò)AOP的引介功能政供,也可以動(dòng)態(tài)地為該業(yè)務(wù)類添加接口的實(shí)現(xiàn)邏輯播聪,讓業(yè)務(wù)類成為這個(gè)接口的實(shí)現(xiàn)類。
  • 織入(Weaving):織入是將增強(qiáng)添加到目標(biāo)類的具體連接點(diǎn)上的過(guò)程布隔,將目標(biāo)類离陶、增強(qiáng)或者引介天衣無(wú)縫地編織到一起。AOP的3種織入方式:
    • 編譯期織入:要求使用特殊的Java編譯器
    • 類裝載期織入:要求使用特殊的類裝載器
    • 動(dòng)態(tài)代理織入:在運(yùn)行期為目標(biāo)類添加增強(qiáng)生成子類的方式
  • 代理(Proxy):一個(gè)類被AOP織入增強(qiáng)之后衅檀,就產(chǎn)生了一個(gè)結(jié)果類枕磁,它是融合了原類和增強(qiáng)邏輯的代理類。根據(jù)不同的代理方式术吝,代理類既可能是和原類具有相同接口的類计济,也可能就是原類的子類,所以可以采用與調(diào)用原類相同的方式調(diào)用代理類排苍。
  • 切面(Aspect):切面由切點(diǎn)和增強(qiáng)(引介)組成沦寂,它既包括橫切邏輯的定義,也包括連接點(diǎn)的定義淘衙。AOP的工作重心在于如何將增強(qiáng)應(yīng)用于目標(biāo)對(duì)象的連接點(diǎn)上传藏,包括以下兩點(diǎn)
    • 通過(guò)切點(diǎn)和增強(qiáng)定位到連接點(diǎn)上
    • 在增強(qiáng)中編寫切面的代碼
1.3.AOP的實(shí)現(xiàn)者
  • AspectJ
  • AspectWerkz
  • JBossAOP
  • SpringAOP

2.基礎(chǔ)知識(shí)

Spring AOP使用動(dòng)態(tài)代理技術(shù)在運(yùn)行時(shí)織入增強(qiáng)代碼,Spring AOP使用兩種代理機(jī)制:一種
基于JDK的動(dòng)態(tài)代理彤守,另一種是基于CGLib的動(dòng)態(tài)代理毯侦。之所以需要兩種代理機(jī)制,是因?yàn)楹艽蟪潭壬螶DK本身只提供接口的代理具垫,而不支持類的代理侈离。

所有實(shí)例的代碼地址
Aop實(shí)例的github地址

2.1.帶有橫切面邏輯的實(shí)例

以下是通過(guò)代碼實(shí)現(xiàn),當(dāng)某個(gè)方法需要進(jìn)行性能監(jiān)視時(shí)筝蚕,必須要調(diào)整方法代碼卦碾,在方法體前后分別添加開啟性能監(jiān)視和結(jié)束性能監(jiān)視的代碼。

public interface ForumService {
    public void removeTopic(int topicID);


    public void removeForum(int topicID);

}

package com.aop.impl;

public class ForumServiceImpl implements ForumService {
    public void removeTopic(int topicID){
        //開始對(duì)該方法進(jìn)行性能監(jiān)視
        PerformanceMonitor.begin("removeTopic");
        System.out.println("模擬刪除論壇topic記錄:"+topicID);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //結(jié)束對(duì)該方法的性能監(jiān)視
        PerformanceMonitor.end();
    }

    public void removeForum(int topicID){
        //開始對(duì)該方法進(jìn)行性能監(jiān)視
        PerformanceMonitor.begin("removeForum");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("模擬刪除Forum記錄:"+topicID);
        //結(jié)束對(duì)該方法的性能監(jiān)視
        PerformanceMonitor.end();
    }
}

public class MethodPerformance {

    private long begin;
    private long end;
    private String serviceMethod;

    public MethodPerformance(String serviceMethod) {
        this.serviceMethod = serviceMethod;
        this.begin = System.currentTimeMillis();//記錄目標(biāo)類方法開始執(zhí)行點(diǎn)的開始時(shí)間

    }

    public void printPerformance() {
        end = System.currentTimeMillis();//獲取目標(biāo)類方法執(zhí)行完成后的系統(tǒng)時(shí)間起宽,進(jìn)而計(jì)算出目標(biāo)類方法的執(zhí)行時(shí)間
        long elapse = end - begin;
        System.out.println(serviceMethod+"花費(fèi)"+elapse+"毫秒");
    }

}

public class PerformanceMonitor {
    //通過(guò)一個(gè)ThreadLocal保存與調(diào)用線程相關(guān)的性能監(jiān)視信息
    private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<>();

    //啟動(dòng)對(duì)某一目標(biāo)方法的性能監(jiān)視
    public static void begin(String method){
        System.out.println("begin monitor....");
        MethodPerformance mp=new MethodPerformance(method);
        performanceRecord.set(mp);
    }

    public static void end(){
        System.out.println("end monitor");
        MethodPerformance mp=performanceRecord.get();
        //打印出方法性能監(jiān)視的結(jié)果信息
        mp.printPerformance();
    }
}

public class TestForumService {
    public static void main(String[] args) {
        ForumService forumService=new ForumServiceImpl();
        forumService.removeForum(10);
        forumService.removeTopic(20);
    }
}



2.2.JDK動(dòng)態(tài)代理

public class PerformanceHandler implements InvocationHandler {

    private Object target;



    public  PerformanceHandler(Object target) {//target為目標(biāo)業(yè)務(wù)類
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName());
        Object obj=method.invoke(target,args);//通過(guò)反射調(diào)用業(yè)務(wù)類的目標(biāo)方法
        PerformanceMonitor.end();
        return obj;
    }

}

public class TestForumService {
    public static void main(String[] args) {
        ForumService target = new ForumServiceImpl();

        PerformanceHandler handler = new PerformanceHandler(target);

        ForumService proxy= (ForumService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);

        proxy.removeForum(10);
        proxy.removeTopic(1020);
    }
}

2.3.CGLib動(dòng)態(tài)代理

由于CGLib采用動(dòng)態(tài)創(chuàng)建子類的方式生成代理對(duì)象洲胖,所以不能對(duì)目標(biāo)類中的final或private方法進(jìn)行代理

public class CGLibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        enhancer.setSuperclass(clazz);//設(shè)置需要?jiǎng)?chuàng)建子類的類
        enhancer.setCallback(this);
        return enhancer.create();//通過(guò)字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//攔截父類所有方法
        PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
        Object result=methodProxy.invokeSuper(obj,objects);//通過(guò)反射調(diào)用業(yè)務(wù)類的目標(biāo)方法
        PerformanceMonitor.end();
        return result;
    }
}

public class TestForumService {

    public static void main(String[] args) {
        CGLibProxy proxy=new CGLibProxy();
        ForumServiceImpl forumService= (ForumServiceImpl) proxy.getProxy(ForumServiceImpl.class);//通過(guò)動(dòng)態(tài)生成子類的方式創(chuàng)建 代理類
        forumService.removeTopic(10);
        forumService.removeForum(1024);

    }
}
2.4.AOP聯(lián)盟
2.5.代理知識(shí)小結(jié)

雖然通過(guò)上面的PerformanceHandler或CGLibProxy實(shí)現(xiàn)了性能監(jiān)視橫切邏輯的動(dòng)態(tài)織入,但是這種實(shí)現(xiàn)方式存在3個(gè)明顯需要改進(jìn)的地方

  • 目標(biāo)類的所有方法都添加了性能監(jiān)視橫切邏輯坯沪,而有時(shí)這并不是我們所期望的绿映,我們可能只希望對(duì)業(yè)務(wù)中的某些特定方法添加橫切面邏輯
  • 通過(guò)硬編碼的方式指定了織入橫切邏輯的織入點(diǎn),即在目標(biāo)類業(yè)務(wù)方法的開始和結(jié)束前織入代碼
  • 手工編寫代理實(shí)例的創(chuàng)建過(guò)程腐晾,在為不同的類創(chuàng)建代理時(shí)叉弦,需要分別編寫相應(yīng)的創(chuàng)建代碼,無(wú)法做到通用

JDK動(dòng)態(tài)代理所創(chuàng)建的代理對(duì)象赴魁,在Java 1.3下卸奉,性能差強(qiáng)人意,研究表明CGLib所創(chuàng)
建的動(dòng)態(tài)代理對(duì)象的性能依舊比JDK所創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能高不少(大概10倍)颖御,但是CGLib在創(chuàng)建代理對(duì)象時(shí)所花費(fèi)的時(shí)間卻比JDK動(dòng)態(tài)代理多(大概8倍)榄棵。對(duì)于singleton的代理對(duì)象或者具有實(shí)例池的代理,因?yàn)闊o(wú)需頻繁的創(chuàng)建代理對(duì)象潘拱,所以比較適合采用CGLib動(dòng)態(tài)代理技術(shù)疹鳄,反之則適合采用JDK動(dòng)態(tài)代理技術(shù)

3.創(chuàng)建增強(qiáng)類

Spring使用增強(qiáng)類定義了橫切面邏輯,同時(shí)由于Spring只支持方法連接點(diǎn)芦岂,增強(qiáng)還包括方法在哪一點(diǎn)加入橫切代碼的方位信息瘪弓,所以增強(qiáng)既包含橫切邏輯,又包含部分連接點(diǎn)的信息

3.1.增強(qiáng)類型
  • 前置增強(qiáng):在目標(biāo)方法前實(shí)施增強(qiáng)
  • 后置增強(qiáng):在目標(biāo)方法后實(shí)施增強(qiáng)
  • 環(huán)繞增強(qiáng):在目標(biāo)方法前后實(shí)施增強(qiáng)
  • 異常拋出增強(qiáng):表示在目標(biāo)方法拋出異常后實(shí)施增強(qiáng)
  • 引介增強(qiáng):在目標(biāo)類中添加一些新的屬性和方法
3.2.前置增強(qiáng)

通過(guò)實(shí)現(xiàn)MethodBeforeAdvice接口


public interface Waiter {

     public void greetTo(String name);
     public void serveTo(String name);
}

public class NativeWaiter implements Waiter {
    @Override
    public void greetTo(String name) {
        System.out.println("greet to "+name+"...");
    }

    @Override
    public void serveTo(String name) {
        System.out.println("serving to "+name+"...");
    }
}

public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object o) throws Throwable {//在目標(biāo)方法調(diào)用前使用
        String clientName= (String) args[0];
        System.out.println("How are you! Mr."+clientName);

    }
}

public class BeforeAdviceTest {

    @Test
    public void before() {
        System.out.println("測(cè)試代碼");
        Waiter target = new NativeWaiter();
        BeforeAdvice advice=new GreetingBeforeAdvice();

        //spring提供代理工廠
        ProxyFactory pf=new ProxyFactory();
        //設(shè)置代理目標(biāo)
        pf.setTarget(target);
        //為代理目標(biāo)添加增強(qiáng)
        pf.addAdvice(advice);
        //生成代理實(shí)例
        Waiter proxy= (Waiter) pf.getProxy();
        proxy.greetTo("John");
        proxy.serveTo("Tom");
    }

}

上面代碼中使用ProxyFactory代理禽最,ProxyFactory內(nèi)部使用的就是JDK或者是CGLib動(dòng)態(tài)代理技術(shù)將增強(qiáng)應(yīng)用到目標(biāo)類中的腺怯。如果通過(guò)ProxyFactory的setInTerfaces方法指定目標(biāo)接口進(jìn)行代理袱饭,則使用JDK代理,如果是針對(duì)類代理呛占,則使用CGLib代理虑乖。此外還可以通過(guò)setOptimize方法讓ProxyFactory啟動(dòng)優(yōu)化代理方式。

3.3.后置增強(qiáng)

后置增強(qiáng)和前置增強(qiáng)的代碼類似晾虑,前置增強(qiáng)要實(shí)現(xiàn)接口MethodBeforeAdvice疹味,后置增強(qiáng)則要實(shí)現(xiàn)接口AfterReturningAdvice

public class GreetingAfterAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("enjoy yourself!");
    }
}
3.4.環(huán)繞增強(qiáng)

環(huán)繞增強(qiáng)綜合實(shí)現(xiàn)了前置、后置增強(qiáng)的功能

public class GreetingInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {//截獲目標(biāo)類方法的執(zhí)行帜篇,并在前后添加橫切邏輯
        Object[] args = methodInvocation.getArguments();
        String clientName = (String) args[0];
        System.out.println("How are you !Mr."+clientName+".");//在目標(biāo)方法前執(zhí)行
        Object obj=methodInvocation.proceed();//通過(guò)反射機(jī)制調(diào)用目標(biāo)方法
        System.out.println("Please enjoy yourself糙捺!");
        return obj;
    }
}

3.5.異常拋出異常

異常拋出異常最適合的應(yīng)用場(chǎng)景是事務(wù)管理,當(dāng)參與事務(wù)的某個(gè)DAO發(fā)生異常時(shí)笙隙,事務(wù)管理器就必須回滾事務(wù)

ThrowAdvice異常拋出增強(qiáng)接口沒有定義任何方法洪灯,它是一個(gè)標(biāo)簽接口,在運(yùn)行期Spring使用反射機(jī)制自行判斷逃沿,必須采用以下簽名形式定義異常拋出的增強(qiáng)方法婴渡。

public class TransactionManager implements ThrowsAdvice {

    //定義增強(qiáng)邏輯
    public void afterThrowing(Method method,Object[] args,Object target,Exception ex) throws Throwable{
        System.out.println("--------------");
        System.out.println("method:"+method.getName());
        System.out.println("拋出異常:"+ex.getMessage());
        System.out.println("成功回滾事務(wù)!");

    }
}

3.6.引介增強(qiáng)

引介增強(qiáng)是為目標(biāo)類創(chuàng)建新的方法和屬性凯亮,所以引介增強(qiáng)的連接點(diǎn)是類級(jí)別的边臼,而非方法級(jí)別的,通過(guò)引介類增強(qiáng)假消,可以為目標(biāo)類添加一個(gè)接口的實(shí)現(xiàn)柠并,即原來(lái)目標(biāo)類未實(shí)現(xiàn)某個(gè)方法接口,通過(guò)引介增強(qiáng)可以為目標(biāo)類創(chuàng)建實(shí)現(xiàn)某接口的代理富拗。

public class ControllablePerformanceMonitor extends DelegatingIntroductionInterceptor implements Monitorable {

    private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<>();


    @Override
    public void setMonitorActive(boolean active) {
        MonitorStatusMap.set(active);
    }

    //攔截方法
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object obj = null;
        //對(duì)于性能監(jiān)視可控代理臼予,通過(guò)判斷其狀態(tài)決定是否開啟性能監(jiān)控功能
        if(MonitorStatusMap.get()!=null&&MonitorStatusMap.get()){
            PerformanceMonitor.begin(mi.getClass().getName()+"."+mi.getMethod().getName());
            obj=super.invoke(mi);
            PerformanceMonitor.end();
        }else {
            obj=super.invoke(mi);
        }
        return obj;
    }
}

4.創(chuàng)建切面

增強(qiáng)被織入目標(biāo)類的所有方法中,假設(shè)我們希望有選擇地織入目標(biāo)類的某些特定的方法中啃沪,就需要使用切點(diǎn)進(jìn)行目標(biāo)連接點(diǎn)的定位粘拾。

Spring通過(guò)Pointcut接口來(lái)描述切點(diǎn),Pointcut由ClassFilter和MethodMatcher構(gòu)成创千,它通過(guò)ClassFilter定位到某些特定類上缰雇,通過(guò)MethodMatcher定位到某些特定方法上,這樣Pointcut就擁有了描述某些類的某些特定方法的能力追驴。

4.1.切點(diǎn)類型
  • 靜態(tài)方法切點(diǎn):匹配所有的類
  • 動(dòng)態(tài)方法切點(diǎn):匹配所有的類
  • 注解切點(diǎn):表示注解切點(diǎn)
  • 表達(dá)式切點(diǎn):支持AspectJ切點(diǎn)表達(dá)式語(yǔ)法而定義的接口
  • 流程切點(diǎn):根據(jù)程序執(zhí)行堆棧的信息查看目標(biāo)方法是否由某一個(gè)方法直接或間接發(fā)起調(diào)用械哟,以此判斷是否為匹配的連接點(diǎn)
  • 復(fù)合切點(diǎn):使用鏈接表達(dá)式對(duì)切點(diǎn)進(jìn)行操作
4.2.切面類型
  • Advisor:代表一般切面,僅包含一個(gè)Advice
  • PointcutAdvisor:代表具有切點(diǎn)的切面殿雪,包含Advice和Pointcut兩個(gè)類
  • IntroductionAdvisor:代表引介切面
4.3.靜態(tài)普通方法名匹配切面
public class GreetingBeforeAdvisor extends StaticMethodMatcherPointcutAdvisor {
    @Override
    public boolean matches(Method method, Class<?> clazz) {
        return "greetTo".equals(method.getName());
    }

    public ClassFilter getClassFilter(){
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return Waiter.class.isAssignableFrom(clazz);
            }
        };
    }
}

4.4.靜態(tài)正則表達(dá)式方法切面
<!--靜態(tài)正則表達(dá)式方法匹配切面-->
<bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
    p:advice-ref="greetingBefore">
 <property name="patterns">
  <list>
    <value>.*greet.*</value><!--定義可匹配模式串暇咆,“.*greet.*”-->
  </list>
</property>
</bean>

<bean id="waiter1"class="org.springframework.aop.framework.ProxyFactoryBean"
    p:proxyTargetClass="true"
    p:interceptorNames="regexpAdvisor"
    p:target-ref="waiterTarget"
/>

4.5.動(dòng)態(tài)切面

Spring會(huì)在創(chuàng)建動(dòng)態(tài)代理織入切面時(shí),對(duì)目標(biāo)類中所有方法進(jìn)行靜態(tài)切點(diǎn)檢查;在生成織入切面的代理對(duì)象后爸业,第一次調(diào)用代理類的每一個(gè)方法時(shí)都會(huì)進(jìn)行一次靜態(tài)切點(diǎn)檢查其骄,如果本次檢查就能從候選者列表中將該方法排除,則以后對(duì)該方法的調(diào)用就不再執(zhí)行靜態(tài)切點(diǎn)檢查沃呢;對(duì)于那些在靜態(tài)切點(diǎn)檢查時(shí)匹配的方法年栓,在后續(xù)調(diào)用該方法時(shí),將執(zhí)行動(dòng)態(tài)切點(diǎn)檢查

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {

    private static List<String> specialClientList=new ArrayList<>();

    static{
        specialClientList.add("Jhon");
        specialClientList.add("Tom");
    }


    public ClassFilter getClassFilter(){
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                System.out.println("調(diào)用getClassFilter()對(duì)"+clazz.getName()+"靜態(tài)檢查.");

                return Waiter.class.isAssignableFrom(clazz);
            }
        };
    }

    @Override
    public boolean matches(Method method, Class<?> clazz, Object... objects) {//對(duì)方法進(jìn)行動(dòng)態(tài)切點(diǎn)檢查
        System.out.println("調(diào)用動(dòng)態(tài)檢查方法對(duì)"+clazz.getName()+"類的"+method+"動(dòng)態(tài)檢查.");
        String clientName= (String) objects[0];
        return specialClientList.contains(clientName);
    }

    @Override
    public boolean matches(Method method, Class<?> clazz) {//對(duì)方法進(jìn)行靜態(tài)切點(diǎn)檢查
        System.out.println("調(diào)用靜態(tài)檢查方法對(duì)"+clazz.getName()+"類的"+method+"靜態(tài)檢查.");
        return "greetTo".equals(method.getName());
    }
}

<!--動(dòng)態(tài)切面-->
<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
  <bean class="cn.com.dynamicPoint.GreetingDynamicPointcut"/>
</property>
<property name="advice">
  <bean class="cn.com.beforeEnhancer.GreetingBeforeAdvice"/>
</property>
</bean>

<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
    p:interceptorNames="dynamicAdvisor"
    p:target-ref="waiterTarget"
    p:proxyTargetClass="true"
/>

每次調(diào)用代理對(duì)象的任何一個(gè)方法薄霜,都會(huì)執(zhí)行動(dòng)態(tài)切點(diǎn)檢查,這將導(dǎo)致很大的性能問(wèn)題纸兔,所以惰瓜,在定義動(dòng)態(tài)切點(diǎn)時(shí),切勿忘記同時(shí)覆蓋getClassFilter()matches(Metho method汉矿,Class clazz)方法崎坊,通過(guò)靜態(tài)切點(diǎn)檢查排除大部分方法

4.6.流程切面

流程切點(diǎn)代表由某個(gè)方法直接或間接發(fā)起調(diào)用的其他方法。流程切面和動(dòng)態(tài)切面從某種程度上來(lái)說(shuō)可以算是一類切面洲拇,因?yàn)槎叨夹枰谶\(yùn)行期判斷動(dòng)態(tài)的環(huán)境奈揍。對(duì)于流程切面來(lái)說(shuō),代理對(duì)象在每次調(diào)用目標(biāo)類方法時(shí)赋续,都需要判斷方法調(diào)用堆棧中是否有滿足流程切點(diǎn)要求的方法男翰,因此,和動(dòng)態(tài)切面一樣纽乱,流程切面對(duì)性能的影響也很大蛾绎。

public class WaiterDelegate {

    private Waiter waiter;

    public void service(String clientName){//waiter的方法通過(guò)該方法發(fā)起調(diào)用
        waiter.serveTo(clientName);
        waiter.greetTo(clientName);
    }

    public void setWaiter(Waiter waiter) {
        this.waiter = waiter;
    }
}

 <!--流程切面-->
  <bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
    <constructor-arg type="java.lang.Class">
      <value>cn.com.flowPoint.WaiterDelegate</value>
    </constructor-arg>
    <constructor-arg type="java.lang.String">
      <value>service</value>
    </constructor-arg>
  </bean>

  <bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
        p:pointcut-ref="controlFlowPointcut"
        p:advice-ref="greetingBefore"
  />

  <bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="controlFlowAdvisor"
        p:target-ref="waiterTarget"
        p:proxyTargetClass="true"
  />
4.7.符合切點(diǎn)切面

有時(shí),一個(gè)切點(diǎn)可能難以描述目標(biāo)連接點(diǎn)的信息鸦列,比如在前面的例子中租冠,假設(shè)我們希望WaiterDelegate#service發(fā)起調(diào)用且被調(diào)用的方法是Waiter#greetTo時(shí)才織入增強(qiáng),這個(gè)切點(diǎn)就是符合切點(diǎn)薯嗤。

public class GreetingComposablePointcut {

    public Pointcut getInTersectionPointcut(){
        ComposablePointcut cp=new ComposablePointcut();//創(chuàng)建一個(gè)復(fù)合切點(diǎn)
        Pointcut pt1=new ControlFlowPointcut(WaiterDelegate.class,"service");//創(chuàng)建一個(gè)流程切點(diǎn)
        NameMatchMethodPointcut pt2=new NameMatchMethodPointcut();//創(chuàng)建方法名切點(diǎn)
        pt2.addMethodName("greetTo");
        return cp.intersection(pt1).intersection((Pointcut) pt2);
    }
}

<!--復(fù)合切點(diǎn)切面-->
  <bean id="gcp" class="cn.com.composablePointcut.GreetingComposablePointcut"></bean>
  <bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
        p:pointcut="#{gcp.inTersectionPointcut}"
        p:advice-ref="greetingBefore"
  />
  <!--引用gcp.inTersectionPointcut方法返回的復(fù)合切點(diǎn)-->

  <bean id="waiter4" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="composableAdvisor"
        p:target-ref="waiterTarget"
        p:proxyTargetClass="true"
  /><!--使用復(fù)合切點(diǎn)-->
4.8.引介切面

5.自動(dòng)創(chuàng)建代理

在前面的例子中顽爹,都通過(guò)ProxyFactoryBean創(chuàng)建織入切面的代理,每個(gè)需要被代理的Bean都需要使用一個(gè)ProxyFactoryBean進(jìn)行配置骆姐,雖然可以使用父子<bean>進(jìn)行改造镜粤,但還是很麻煩,Spring提供了自動(dòng)代理機(jī)制诲锹,讓容器自動(dòng)生成代理繁仁,可以從繁瑣的配置中解放出來(lái)。Spring使用BeanPostProcessor自動(dòng)完成這項(xiàng)工作

5.1.實(shí)現(xiàn)類介紹

這些基于BeanPostProcessor的自動(dòng)代理創(chuàng)建器的實(shí)現(xiàn)類归园,將根據(jù)一些規(guī)則自動(dòng)在容器實(shí)例化Bean時(shí)為匹配的Bean生成自動(dòng)代理實(shí)例黄虱。

  • 基于Bean配置名規(guī)則的自動(dòng)代理創(chuàng)建器:允許為一組特定配置名的Bean自動(dòng)創(chuàng)建代理實(shí)例的代理創(chuàng)建器,實(shí)現(xiàn)類為BeanNameAutoProxyCreator
  • 基于Advisor匹配機(jī)制的自動(dòng)代理創(chuàng)建器:它會(huì)對(duì)容器中所有的Advisor進(jìn)行掃描庸诱,自動(dòng)將這些切面應(yīng)用到匹配的Bean中(為目標(biāo)Bean創(chuàng)建帶實(shí)例)捻浦,實(shí)現(xiàn)類為DefaultAdvisorAutoProxyCreator
  • 基于Bean中AspectJ注解標(biāo)簽的自動(dòng)代理創(chuàng)建器:為包含AspectJ注解的Bean自動(dòng)創(chuàng)建代理實(shí)例晤揣,實(shí)現(xiàn)類為AnnotationAwareAspectJAutoProxyCreator
5.2.BeanNameAutoProxyCreator
<!--自動(dòng)代理BeanNameAutoProxyCreator-->
  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
        p:beanNames="*er"
        p:interceptorNames="greetingBefore"
        p:optimize="true"
  />

5.3.DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator能夠掃描容器中的Advisor,并將Advisor自動(dòng)織入匹配的目標(biāo)Bean中朱灿,即為匹配的目標(biāo)Bean自動(dòng)創(chuàng)建代理

<bean id="regexpAdvisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
        p:patterns=".*greet.*"
        p:advice-ref="greetingBefore"
  />
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
5.4.AOP無(wú)法增強(qiáng)疑難問(wèn)題剖析

因?yàn)锳OP底層的實(shí)現(xiàn)原理基于JDK和CGLib動(dòng)態(tài)代理昧识,在JDK動(dòng)態(tài)代理中通過(guò)接口來(lái)實(shí)現(xiàn)方法攔截,所以必須保證要攔截的目標(biāo)方法在接口中有定義盗扒,在CGLib動(dòng)態(tài)代理中通過(guò)動(dòng)態(tài)生成代理子類來(lái)實(shí)現(xiàn)方法攔截跪楞,所以必須要確保攔截的目標(biāo)方法可被子類訪問(wèn),也就是目標(biāo)方法必須定義為非final侣灶,即非私有實(shí)例方法

<bean id="regexpAdvisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
        p:pattern=".*To.*"
        p:advice-ref="greetingBefore"
  />
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
  <!--修改一下自動(dòng)代理的正則表達(dá)式甸祭,所有方法中包含To的都要織入增強(qiáng),-->
  
  
  public class NativeWaiter implements Waiter {
    @Override
    public void greetTo(String name) {
        //throw new RuntimeException("運(yùn)行異常");
        System.out.println("greet to "+name+"...");
    }

    @Override
    public void serveTo(String name) {
        greetTo(name);
        System.out.println("serving to "+name+"...");
    }
    
    //只調(diào)用serveTo方法褥影,serverTo和greetTo方法能否可以同時(shí)增強(qiáng)
    /**
    console打印的日志如下
    
    How are you! Mr.Jhon
    greet to Jhon...
    serving to Jhon...
    
    表明被調(diào)用的方法沒有織入增強(qiáng)
    **/
}

>在調(diào)用內(nèi)部方法時(shí)池户,讓其通過(guò)代理類調(diào)用內(nèi)部的方法來(lái)解決內(nèi)部方法不能被代理的情況,因此凡怎,需要讓原來(lái)的Waiter實(shí)現(xiàn)一個(gè)可注入自身代理類的接口

注:通過(guò)配置代理類來(lái)調(diào)用內(nèi)部方法還是不能夠給方法織入增強(qiáng)校焦,關(guān)于這點(diǎn)如果有大神知道,請(qǐng)告知统倒,我后面也會(huì)持續(xù)研究這個(gè)問(wèn)題寨典,如果有進(jìn)展會(huì)第一時(shí)間更新文章

Aop實(shí)例的github地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市檐薯,隨后出現(xiàn)的幾起案子凝赛,更是在濱河造成了極大的恐慌,老刑警劉巖坛缕,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墓猎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡赚楚,警方通過(guò)查閱死者的電腦和手機(jī)毙沾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宠页,“玉大人左胞,你說(shuō)我怎么就攤上這事【倩В” “怎么了烤宙?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)俭嘁。 經(jīng)常有香客問(wèn)我躺枕,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任拐云,我火速辦了婚禮罢猪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叉瘩。我一直安慰自己膳帕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布薇缅。 她就那樣靜靜地躺著危彩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泳桦。 梳的紋絲不亂的頭發(fā)上恬砂,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音蓬痒,去河邊找鬼。 笑死漆羔,一個(gè)胖子當(dāng)著我的面吹牛梧奢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播演痒,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼亲轨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鸟顺?” 一聲冷哼從身側(cè)響起惦蚊,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎讯嫂,沒想到半個(gè)月后蹦锋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡欧芽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年莉掂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片千扔。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡憎妙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出曲楚,到底是詐尸還是另有隱情厘唾,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布龙誊,位于F島的核電站抚垃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讯柔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一抡蛙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧魂迄,春花似錦粗截、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至湿酸,卻和暖如春婿屹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背推溃。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工昂利, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铁坎。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓蜂奸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親硬萍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扩所,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容