• <acronym id="qmqcg"><cite id="qmqcg"></cite></acronym>
    <td id="qmqcg"><em id="qmqcg"></em></td>
    • 首頁 > 生活 >

      【Spring源碼】- 10 Spring AOP核心API

      概述

      Spring的兩大核心:IoCAOPIoC作為Spring的根基,通過大量的擴(kuò)展點(diǎn)讓系統(tǒng)輕而易舉的就可以實(shí)現(xiàn)良好的擴(kuò)展性,而AOPIoC結(jié)合在一起,類似于發(fā)生強(qiáng)大化學(xué)反應(yīng)一樣,將Spring的功能性又提高了一個(gè)層次。Spring中也有大量使用AOP場景,比如@Configuration、數(shù)據(jù)庫事務(wù)、mybatis mapper接口注入等等。


      (資料圖片)

      AOP全稱Aspect Oriented Programming,即面向切面編程,其并非Spring獨(dú)有,作為一種對(duì)OOP編程思想的補(bǔ)充,其也有自己的標(biāo)準(zhǔn)規(guī)范并有獨(dú)立的組織進(jìn)行維護(hù)。

      根據(jù)織入時(shí)機(jī)的不同,AOP又可以分為三類:

      編譯時(shí)織入:ApectJ主要采用的就是編譯時(shí)織入方式,這種一般使用特定的編譯器方式實(shí)現(xiàn);類加載時(shí)織入:這種一般都是依賴JVM Instruments技術(shù)實(shí)現(xiàn),Spring中也有對(duì)這種技術(shù)支持,具體可以了解下LoadTimeWeaver;動(dòng)態(tài)織入:動(dòng)態(tài)代理方式實(shí)現(xiàn)AOP就是動(dòng)態(tài)織入場景,Spring中實(shí)現(xiàn)AOP最主要方式,根據(jù)動(dòng)態(tài)代理方式不同,又可以分為:JDK動(dòng)態(tài)代理CGLIB動(dòng)態(tài)代理。

      AOP標(biāo)準(zhǔn)規(guī)范是由獨(dú)立的組織機(jī)構(gòu)進(jìn)行維護(hù),其涉及到的核心概念主要如下:

      連接點(diǎn)(JoinPoint):程序運(yùn)行中的某個(gè)階段點(diǎn),比如方法的調(diào)用、異常的拋出、類初始化和對(duì)象實(shí)例化等。連接點(diǎn)是AOP的核心概念,并且定義了在應(yīng)用程序中可以使用AOP插入其它邏輯的點(diǎn);切點(diǎn)(Pointcut):切點(diǎn)是基于規(guī)則定義如何查找連接點(diǎn),其可以看成包含一系列連接點(diǎn)的組合,Spring中對(duì)應(yīng)的是Pointcut接口,定義了哪些類的哪些方法需要織入增強(qiáng);通知(Advice):在連接點(diǎn)處需要織入的增強(qiáng)代碼邏輯封裝;切面(Aspect):切面是AdvicePointcut組合,對(duì)應(yīng)SpringAdvisor;織入(Weaving):織入是在適當(dāng)?shù)奈恢脤⑶忻娌迦氲綉?yīng)用程序代碼中的過程,就是上面說的編譯時(shí)織入、類加載時(shí)織入和動(dòng)態(tài)織入;目標(biāo)對(duì)象(target):AOP代理增強(qiáng)的原生對(duì)象;

      基礎(chǔ)API

      Spring AOP很多人不能很好的理解、使用,一方面是因?yàn)?code>AOP涉及的概念可能比較抽象,不容易理解;另外一方面你對(duì)Spring AOP涉及到的一些基礎(chǔ)API不熟悉。下面我們就對(duì)Spring AOP中最核心的一些API,由底向上,由基礎(chǔ)到高級(jí)方式一步步分析。

      Enhancer

      Spring AOP主要使用的是動(dòng)態(tài)代理方式實(shí)現(xiàn),動(dòng)態(tài)代理實(shí)現(xiàn)主要包括兩種:jdk動(dòng)態(tài)代理cglib動(dòng)態(tài)代理。jdk動(dòng)態(tài)代理方式比較熟悉,下面就來看下cglib動(dòng)態(tài)代理如何實(shí)現(xiàn)。

      Spring中提供了一個(gè)工具類:Enhancer,Spring中主要就是利用該工具類創(chuàng)建cglib動(dòng)態(tài)代理。下面我們通過一個(gè)案例看下其基本使用:

      1、創(chuàng)建Callback回調(diào)接口類,該接口中就可以實(shí)現(xiàn)增強(qiáng)邏輯:

      public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)   throws Throwable {  try {   before(method);//前置通知   Object ret = methodProxy.invokeSuper(obj, args);//目標(biāo)方法執(zhí)行   after(method, ret);//后置通知   return ret;  } catch (Exception e) {   exception();//異常通知  } finally {   afterReturning();//方法返回通知  }  return null; } //前置增強(qiáng) private void before(Method method) {  System.out.printf("before execute:%s\r\n", method.getName()); } //后置增強(qiáng) private void after(Method method, Object ret) {  System.out.printf("after execute:%s, ret:%s\r\n", method.getName(), ret); } //異常增強(qiáng) private void exception() {  System.out.println("execute failure"); } //after返回增強(qiáng) private void afterReturning() {  System.out.println("execute finish"); }    }

      2、編寫測試:

      //NoOp.INSTANCE:NoOp回調(diào)把對(duì)方法調(diào)用直接委派給這個(gè)方法在父類中的實(shí)現(xiàn),即可理解為真實(shí)對(duì)象直接調(diào)用方法,沒有任何增強(qiáng)private static final Callback[] CALLBACKS = new Callback[] {            new MyMethodInterceptor(),            NoOp.INSTANCE};public void test()  {    //創(chuàng)建Enhancer實(shí)例    Enhancer enhancer =  new Enhancer();        //cglib是基于繼承方式代理,superClass就是基于哪個(gè)類型父類進(jìn)行增強(qiáng),創(chuàng)建出來的對(duì)象就是該類型子類    enhancer.setSuperclass(UserServiceImpl.class);    //CallbackFilter主要用于過濾不同Method使用不同的Callback    enhancer.setCallbackFilter(new CallbackFilter() {        @Override        public int accept(Method method) {            if (method.getDeclaringClass() == Object.class) {                return 1;//使用Callback數(shù)組下標(biāo)是1的            }            return 0;//使用Callback數(shù)組下標(biāo)是0的        }    });    //設(shè)置Callback數(shù)組,Callback就是封裝的增強(qiáng)邏輯    enhancer.setCallbacks(CALLBACKS);    //創(chuàng)建代理對(duì)象    UserService proxyObj = (UserService) enhancer.create();    System.out.println(proxyObj.say("zhangsan"));}

      通過上面enhancer.create()這條語句,就可以為目標(biāo)類創(chuàng)建一個(gè)cglib動(dòng)態(tài)代理,通過Callback回調(diào)方式將各種增強(qiáng)邏輯織入到代理實(shí)例中。

      還可以使用Enhancer.createClass()方法只創(chuàng)建出代理類型,然后自己通過反射方式創(chuàng)建對(duì)象。這時(shí),需要注意:1、這時(shí)就不能使用setCallbacks()設(shè)置Callback數(shù)組,而是使用setCallbackTypes()設(shè)置Callback對(duì)應(yīng)的類型;2、Enhancer.createClass()執(zhí)行完成后,再通過Enhancer.registerStaticCallbacks(clz, CALLBACKS)方式設(shè)置Callback數(shù)組;

      enhancer.setInterfaces()可用于設(shè)置生成的代理類必須實(shí)現(xiàn)的接口,比如你可以不設(shè)置superclass,只設(shè)置interfaces,這時(shí)也是可以正常創(chuàng)建出基于這個(gè)接口的動(dòng)態(tài)代理實(shí)例,但是這時(shí)就要注意不能觸發(fā)目標(biāo)對(duì)象方法執(zhí)行,如methodProxy.invokeSuper執(zhí)行會(huì)報(bào)錯(cuò),如下:

      public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)  throws Throwable { try {  before(method);//前置通知  //Object ret = methodProxy.invokeSuper(obj, args);//目標(biāo)方法執(zhí)行  after(method, ret);//后置通知  return ret; } catch (Exception e) {  exception();//異常通知 } finally {  afterReturning();//方法返回通知 } return null;}

      基于接口創(chuàng)建的代理實(shí)例還是非常有用的,比如mybatis mapper就是一個(gè)沒有實(shí)現(xiàn)類的接口,但是在spring中卻可以依賴注入到service bean中,其中就是利用到上面基于接口創(chuàng)建動(dòng)態(tài)代理的思想,注入進(jìn)來的其實(shí)就是基于接口的動(dòng)態(tài)代理,然后調(diào)用接口中方法時(shí)就可以進(jìn)行攔截,獲取到具體調(diào)用方法簽名信息以及參數(shù)信息,基于這些數(shù)據(jù)進(jìn)行業(yè)務(wù)邏輯處理。

      invoke和invokeSuper方法區(qū)別

      Callback#intercept()回調(diào)方法中執(zhí)行methodProxy.invokeSuper()methodProxy.invoke()是有很大區(qū)別的,而且看不到執(zhí)行流程,所以這里涉及的邏輯非常繞。

      public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)  throws Throwable { Object ret = methodProxy.invokeSuper(obj, args);    Object ret = methodProxy.invoke(delete, args);}

      大致區(qū)別如下圖:

      客戶端觸發(fā)代理對(duì)象say方法調(diào)用,首先進(jìn)入代理對(duì)象中的同名方法,然后進(jìn)入方法攔截對(duì)象MethodInterceptor,這里會(huì)出現(xiàn)兩種情況:

      如果調(diào)用invokeSuper方法,流程會(huì)重新走到代理對(duì)象中,代理對(duì)象這時(shí)會(huì)識(shí)別出是調(diào)用super中同名方法,所以沒有繼續(xù)向下走,而是通過super.say()方式調(diào)用目標(biāo)對(duì)象中的方法。如果調(diào)用invoke方法,代理對(duì)象內(nèi)部其實(shí)包裹一個(gè)目標(biāo)對(duì)象target,這時(shí)它是直接通過target.say方式調(diào)用目標(biāo)對(duì)象。

      invokeSuper()invoke()方法都可以調(diào)用到目標(biāo)對(duì)象方法,但是它們之間存在的一個(gè)本質(zhì)區(qū)別:上下文環(huán)境不一樣;或者更直接說:目標(biāo)對(duì)象中this指向不一樣。通過super.say()方式調(diào)用的目標(biāo)對(duì)象,this指向的是代理對(duì)象;而通過target.say()方式調(diào)用的,目標(biāo)對(duì)象中this指向的就是目標(biāo)對(duì)象本身。這會(huì)導(dǎo)致什么差異呢?

      假如目標(biāo)對(duì)象類型如下定義,然后使用Enhancer創(chuàng)建一個(gè)代理對(duì)象:

      public class Target {        public void a() {        System.out.println(" a 方法");        b();    }        public void b() {        System.out.println(" b 方法");    }}

      客戶端觸發(fā)代理對(duì)象a()方法執(zhí)行,如果攔截器中使用invoke方式調(diào)用目標(biāo)對(duì)象:直接調(diào)用目標(biāo)對(duì)象a()方法,這個(gè)方法中又會(huì)通過this.b()調(diào)用方法b,由于是目標(biāo)對(duì)象本身內(nèi)部調(diào)用,所以b()方法不會(huì)被攔截的。

      客戶端觸發(fā)代理對(duì)象a()方法執(zhí)行,如果攔截器中使用invokeSuper()方式調(diào)用目標(biāo)對(duì)象:這里是通過super.a()方式調(diào)用目標(biāo)對(duì)象中的a()方法,然后a()方法又會(huì)通過this.b()調(diào)用方法b,注意這時(shí)的this不是目標(biāo)對(duì)象本身,而是代理對(duì)象,因?yàn)榇韺?duì)象繼承目標(biāo)對(duì)象,代理對(duì)象會(huì)有重名方法覆寫了目標(biāo)對(duì)象方法。所以,this.b()實(shí)際上會(huì)觸發(fā)代理對(duì)象中方法b的執(zhí)行,這時(shí)是會(huì)觸發(fā)攔截器的。

      所以,methodProxy.invokeSuper(obj, args)這個(gè)obj是代理對(duì)象;而methodProxy.invoke(obj, args)這個(gè)入?yún)?code>obj是目標(biāo)對(duì)象。搞清楚這些基本理解清楚應(yīng)該使用invoke還是invokeSuper。

      ProxyFactory

      Enhancer只能用于創(chuàng)建cglib動(dòng)態(tài)代理,Spring中還有一個(gè)更上層點(diǎn)的類ProxyFactory,可以用于創(chuàng)建JDK動(dòng)態(tài)代理cglib動(dòng)態(tài)代理。

      public void test02() { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(new UserServiceImpl());    /*    調(diào)用ProxyFactory.addInterface()或setInterfaces()表示對(duì)接口進(jìn)行代理,一般會(huì)使用jdk動(dòng)態(tài)代理,    除非setOptimize(true)或setProxyTargetClass(true)表示使用cglib代理    cglib代理類名稱格式大致為:ServiceImpl$$EnhancerBySpringCGLIB$$f2952b94    */ //setInterfaces設(shè)置這個(gè),會(huì)基于接口代理,使用jdk動(dòng)態(tài)代理方式 proxyFactory.setInterfaces(UserService.class); //proxyFactory.setOptimize(true); //proxyTargetClass=true:直接代理目標(biāo)類,而不是接口,使用cglib proxyFactory.setProxyTargetClass(true); // 添加增強(qiáng) proxyFactory.addAdvice(new MyBeforeAdvice02());    //內(nèi)部使用了緩存,target不變時(shí),getProxy()獲取到的都是同一個(gè)    //只有target變化時(shí),才會(huì)重新創(chuàng)建新的代理對(duì)象 Object proxy = proxyFactory.getProxy();}

      ProxyFactory類是AOP底層實(shí)現(xiàn)中非常重要的一個(gè)類,另外AnnotationAwareAspectJAutoProxyCreatorBeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator等一些高級(jí)AOP實(shí)現(xiàn)工具類都是通過在其父類AbstractAutoProxyCreator中借助ProxyFactory實(shí)現(xiàn)AOP邏輯織入的。所以,理解ProxyFactory的使用對(duì)理解Spring AOP至關(guān)重要。

      ProxyFactory類控制代理的創(chuàng)建過程,其內(nèi)部委托給DefaultAopProxyFactory的一個(gè)實(shí)例,該實(shí)例又轉(zhuǎn)而委托給Cglib2AopProxyJdkDynamicAopProxy,用于創(chuàng)建基于cglib代理還是jdk代理,想了解這兩種動(dòng)態(tài)代理區(qū)別可以分析下這個(gè)類源碼。

      ProxyFactoryaddAdvice()方法將傳入的通知封裝到DefaultPointcutAdvisor(DefaultPointcutAdvisorPointcutAdvisor的標(biāo)準(zhǔn)實(shí)現(xiàn))的一個(gè)實(shí)例中,并使用默認(rèn)包含所有方法的切入點(diǎn)對(duì)其進(jìn)行配置。為更加靈活細(xì)粒度的控制在哪些連接點(diǎn)上攔截通知,可以使用addAdVisor()方法添加一個(gè)帶有切入點(diǎn)消息的Advisor

      可以使用相同的ProxyFactory實(shí)例來創(chuàng)建多個(gè)代理,每個(gè)代理都有不同的切面。為了幫助實(shí)現(xiàn)該過程,ProxyFactory提供了removeAdvice()removeAdvisor()方法,這些方法允許從ProxyFactory中刪除之前傳入的任何通知或切面,同時(shí)可以使用boolean adviceIncluded(@Nullable Advice advice)檢查ProxyFactory是否附有特定的通知對(duì)象。

      Advice

      ProxyFactoryaddAdvice()addAdvisor()兩個(gè)方法分別引入了兩個(gè)重要的類:AdviceAdvisor。首先,我們來看下Advice這個(gè)接口類,其可以看成需要織入增強(qiáng)的代碼邏輯封裝。AdviceSpringAPI結(jié)構(gòu)如下:

      大致描述:

      BeforeAdvice:前置通知,該接口沒有任何方法,平時(shí)主要使用其子類MethodBeforeAdvice,因?yàn)镾pring中只能在方法前后織入,對(duì)應(yīng)注解@Before;AfterAdvice:后置通知,其存在兩個(gè)子類AfterReturningAdviceThrowsAdvice,分別對(duì)應(yīng)@AfterReturning@AfterThrowingMethodInterceptor:可以實(shí)現(xiàn)環(huán)繞通知,對(duì)應(yīng)注解@Around;

      Advisor

      AOP規(guī)范中有切面概念,在Spring中大概對(duì)應(yīng)就是Advisor。Advisor有兩個(gè)子接口:PointcutAdvisorIntroductionAdvisor

      其實(shí)真正使用比較多的是它的子類PointcutAdvisor,該接口關(guān)鍵就是如下兩個(gè)方法:

      public interface PointcutAdvisor { Advice getAdvice(); Pointcut getPointcut();}

      PointcutAdvisor從接口定義大概就可以看出,其就是對(duì)AdvicePointcut的封裝,Advice代表的是橫切面需要織入的代碼,而Pointcut定義了如何去切的問題。從之前分析來看,Advice也可以看出一種非常簡單的切面,是對(duì)指定的類所有方法都進(jìn)行切入,橫切面太寬泛,靈活性不夠,PointAdvisor引入了Pointcut后顯然比Advice更加靈活、強(qiáng)大。

      PointcutAdvisor主要有6個(gè)具體的實(shí)現(xiàn)類,分別介紹如下:

      DefaultPointcutAdvisor:最常用的切面類型,它可以通過任意PointcutAdvice定義一個(gè)切面,一般可以通過擴(kuò)展該類實(shí)現(xiàn)自定義的切面;NameMatchMethodPointcutAdvisor:通過該類可以定義按方法名定義切點(diǎn)的切面;RegexpMethodPointcutAdvisor:使用正則表達(dá)式模式定義切點(diǎn),其內(nèi)部通過JdkRegexpMethodPointcut構(gòu)造出正則表達(dá)式方法名切點(diǎn);StaticMethodMatcherPointcutAdvisor:靜態(tài)方法匹配器切點(diǎn)定義的切面,默認(rèn)情況下,匹配所有的目標(biāo)類;AspecJExpressionPointcutAdvisor:用于Aspecj切點(diǎn)表達(dá)式定義切點(diǎn)的切面;AspecJPointcutAdvisor:用于AspecJ語法定義切點(diǎn)的切面;

      其實(shí),這些Advisor主要區(qū)別還是基于其內(nèi)部封裝的Pointcut實(shí)現(xiàn)類體現(xiàn)的,在實(shí)際工作中這些類使用的可能不多,這里的核心在于Pointcut如何定義切入點(diǎn),所以實(shí)際開發(fā)中更多的可能會(huì)去定制Pointcut實(shí)現(xiàn)類,然后使用DefaultPointcutAdvisor將其包裝成Advisor使用。

      下面通過RegexpMethodPointcutAdvisor案例簡單了解即可:

      public void test1(){    AnnotationConfigApplicationContext context =            new AnnotationConfigApplicationContext("aop.demo03");    UserServiceImpl target = new UserServiceImpl();    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();    proxyFactoryBean.setTarget(target);    proxyFactoryBean.setProxyTargetClass(true);    RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();    //設(shè)置advisor的advice    advisor.setAdvice(new MyBeforeAdvice02());    //設(shè)置advisor的pointcut,aop.demo03包下所有類中已say開頭的方法才會(huì)織入    advisor.setPattern("aop.demo03..*.say*");    proxyFactoryBean.addAdvisor(advisor);    proxyFactoryBean.setBeanFactory(context);    Object obj = proxyFactoryBean.getObject();    System.out.println(obj.getClass().getName());    UserServiceImpl userService = (UserServiceImpl) obj;    System.out.println(userService.say("haha"));}

      RegexpMethodPointcutAdvisor表示通過正則表達(dá)式進(jìn)行切點(diǎn)描述的切面,它有一個(gè)pattern屬性用來指定增強(qiáng)要應(yīng)用到哪些類的哪些方法,也可以通過patterns屬性指定多個(gè)表達(dá)式進(jìn)行匹配。有一個(gè)advice屬性用來表示要應(yīng)用的增強(qiáng),這樣就能表示一個(gè)完整的切面了。

      Pointcut

      Advisor引入了一個(gè)核心接口Pointcut,其描述了對(duì)哪些類的哪些方法進(jìn)行切入。Pointcut從其定義可以看出其由ClassFilterMethodMatcher構(gòu)成。ClassFilter用于定位哪些類可以進(jìn)行切入,然后再通過MethodMatcher定位類上的哪些方法可以進(jìn)行切入,這樣Pointcut就擁有了識(shí)別哪些類的哪些方法能被進(jìn)行切入的能力。

      public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher();}

      Pointcut接口API結(jié)構(gòu)見下:

      其中這里比較常用的AnnotationMatchingPointcut基于注解進(jìn)行切入,之前分析【Spring源碼】- 09 擴(kuò)展點(diǎn)之@Import注解一節(jié)就使用到該實(shí)現(xiàn)類,將標(biāo)記有@MyAsync注解的方法都進(jìn)行增強(qiáng)就是利用這個(gè)實(shí)現(xiàn)類:

      AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MyAsync.class);Advice advice = new AsyncAnnotationAdvice(executor);DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();advisor.setPointcut(pointcut);advisor.setAdvice(advice);ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(bean);if(!this.isProxyTargetClass()){ proxyFactory.setInterfaces(bean.getClass().getInterfaces());}proxyFactory.addAdvisor(advisor);proxyFactory.copyFrom(this);return proxyFactory.getProxy();

      ProxyFactoryBean

      ProxyFactoryBeanProxyFactory功能和使用其實(shí)差不多,底層邏輯也基本一致,ProxyFactoryBean主要是融合了IOC功能。一方面ProxyFactoryBean類是FactoryBean的一個(gè)實(shí)現(xiàn),更加方便注入IoC中,參照mybatisspring整合一節(jié),其中關(guān)鍵一步就是將掃描的BeanDefinitionbeanClass由接口類替換成FactoryBean類型;另一點(diǎn)就是切面可以使用IoC容器bean。

      下面通過一個(gè)案例簡單看下ProxyFactoryBean使用:

      package org.simon.ioc.demo1;import org.springframework.aop.MethodBeforeAdvice;import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Componentpublic class MyBeforeAdvice implements MethodBeforeAdvice { public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {  System.out.println("-----洗手-----"); }}
      @Testpublic void test1(){ AnnotationConfigApplicationContext context =    new AnnotationConfigApplicationContext("org.simon.ioc.demo1"); UserService target = new UserServiceImpl(); ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); proxyFactoryBean.setTarget(target); proxyFactoryBean.setInterceptorNames("myBeforeAdvice", "otherAdvice*"); proxyFactoryBean.setBeanFactory(context); Object obj = proxyFactoryBean.getObject(); System.out.println(obj.getClass().getName()); UserService userService = (UserService) obj; System.out.println(userService.say("haha"));}

      其中關(guān)鍵一步在:proxyFactoryBean.setInterceptorNames("myBeforeAdvice", "otherAdvice*");,可以使用IoC中的beanName,同時(shí)還支持通配符*IoC中查找對(duì)應(yīng)Bean。

      高級(jí)API

      前面介紹的類、接口等都是Spring AOP中一些底層API,使用起來不太方便,感覺功能不太強(qiáng)大,不論是ProxyFactory還是ProxyFactoryBean創(chuàng)建織入切面的代理,每次只能硬編碼一個(gè)具體的Bean,假如我想將某個(gè)包路徑下符合一定規(guī)則的類的特定方法都進(jìn)行織入代理怎么辦?

      使用前面那些API好像都不能實(shí)現(xiàn)這個(gè)需求,但是結(jié)合之前分析的Spring擴(kuò)展點(diǎn),很容易想到可以結(jié)合BeanPostProcessor擴(kuò)展點(diǎn)實(shí)現(xiàn)這個(gè)需求,postProcessAfterInitialization()這個(gè)方法回調(diào)時(shí)Bean依賴注入、初始化等都已經(jīng)完成,這時(shí)就可以在這個(gè)方法中過濾出符合一定條件的Bean進(jìn)行代理增強(qiáng)處理。

      其實(shí),在Spring中基于這種思想,已經(jīng)為我們提供了三個(gè)實(shí)現(xiàn)類:

      BeanNameAutoProxyCreator:基于beanName進(jìn)行織入增強(qiáng),通過setBeanNames(String... beanNames)將需要增強(qiáng)的beanName設(shè)置進(jìn)去;DefaultAdvisorAutoProxyCreator:基于Advisor匹配機(jī)制進(jìn)行織入增強(qiáng),它會(huì)對(duì)容器中所有的Advisor進(jìn)行掃描,自動(dòng)將這些切面應(yīng)用到匹配的Bean中;AnnotationAwareAspectjAutoProxyCreator:基于BeanAspectJ注解方式進(jìn)行織入增強(qiáng),就是實(shí)現(xiàn)平時(shí)使用的@Aspect@Before@AroundAspectJ注解功能支持。

      下面我們就來通過DefaultAdvisorAutoProxyCreator了解下使用場景:

      1、定義一個(gè)目標(biāo)類,后續(xù)就是基于該類進(jìn)行增強(qiáng):

      @Componentpublic class UserServiceImpl{ public String say(String name){  System.out.println("執(zhí)行:==UserService#say===");  return "hello,"+name; }}

      2、定義一個(gè)配置類:

      @Configuration@ComponentScan(basePackageClasses = AopConfig.class)public class AopConfig {    @Bean    public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){        RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();        advisor.setAdvice(new MyBeforeAdvice02());        //aop.demo03包下所有類中帶有say開頭方法進(jìn)行增強(qiáng)        advisor.setPattern("aop.demo03..*.say*");        return advisor;    }    @Bean    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){        return new DefaultAdvisorAutoProxyCreator();    }}

      這個(gè)類有兩個(gè)關(guān)鍵,一個(gè)是向IoC中注入Advisor,之前分析過Advisor包含兩個(gè)功能:

      通過Pointcut定位哪些類的哪些方法需求切入;通過關(guān)聯(lián)的Advice指定切入增強(qiáng)邏輯;

      另一個(gè)關(guān)鍵就是注入DefaultAdvisorAutoProxyCreator,這個(gè)就是一個(gè)Spring內(nèi)置的實(shí)現(xiàn)BeanPostProcessor擴(kuò)展類,其在postProcessAfterInitialization()方法中對(duì)Bean進(jìn)行切入增強(qiáng)。

      3、測試:

      public void test1(){        AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(AopConfig.class);        System.out.println(context.getBean(UserServiceImpl.class).getClass());}

      context.getBean(UserServiceImpl.class)IoC容器中獲取的就是使用cglib代理后的實(shí)例。

      下面我們再來分析下AnnotationAwareAspectjAutoProxyCreator,平時(shí)如果項(xiàng)目中需要開啟AOP功能,使用@EnableAspectJAutoProxy注解方式開啟,我們來看下該注解干了什么?

      1、@EnableAspectJAutoProxy注解使用@Import注解將AspectJAutoProxyRegistrar引入:

      @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class)public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false;}

      2、AspectJAutoProxyRegistrarImportBeanDefinitionRegistrar接口實(shí)現(xiàn)類:

      class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(   AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {  //注冊一個(gè)SmartInstantiationAwareBeanPostProcessor類型的實(shí)現(xiàn)類:AnnotationAwareAspectJAutoProxyCreator  AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);  AnnotationAttributes enableAspectJAutoProxy =    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);  if (enableAspectJAutoProxy != null) {   if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);   }   if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);   }  } }}

      其中最關(guān)鍵一句AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);,就是向IoC容器中注入AnnotationAwareAspectJAutoProxyCreator

      這樣我們就明白了@EnableAspectJAutoProxy注解方式開啟AOP的本質(zhì)就像向IoC中注入AnnotationAwareAspectJAutoProxyCreator,它利用BeanPostProcessor擴(kuò)展點(diǎn)功能實(shí)現(xiàn)織入增強(qiáng)邏輯。

      總結(jié)

      首先,對(duì)Spring AOP底層一些最基礎(chǔ)、最核心的API的分析梳理,相信你會(huì)對(duì)Spring AOP底層實(shí)現(xiàn)邏輯有了一個(gè)更加深入的理解。然后通過Spring AOP提供的高級(jí)API,理解了如何將IoCAOP集成到一起實(shí)現(xiàn)強(qiáng)大功能,對(duì)SpringAOP的整體實(shí)現(xiàn)思路也有了比較清晰的認(rèn)識(shí)。

      關(guān)鍵詞:

      責(zé)任編輯:Rex_28

      推薦閱讀
      亚洲线精品久久一区二区三区,成人看片在线观看,草草视频手机在线观看视频,亚洲六月丁香色婷婷综合久久
    • <acronym id="qmqcg"><cite id="qmqcg"></cite></acronym>
      <td id="qmqcg"><em id="qmqcg"></em></td>
      • 主站蜘蛛池模板: 在线观看中文字幕第一页| 亚洲伊人久久大香线蕉在观| 伊人影院在线视频| 久久精品国产亚洲一区二区 | 337p色噜噜| 男女同房猛烈无遮挡动态图| 漂亮人妻被黑人久久精品| 成熟女人牲交片免费观看视频| 国产精品99久久久久久猫咪| 人人狠狠综合久久亚洲| 中字幕视频在线永久在线| 麻豆国产AV丝袜白领传媒 | xxxx日本在线| 波多野结衣潜入搜查官| 妞干网2018| 国产va免费精品观看精品| 久久青青草原国产精品免费| 18禁白丝喷水视频www视频| 涂了媚药的玉势| 国产香蕉一区二区三区在线视频| 免费成人在线网站| 丝袜诱惑中文字幕| 美女视频一区二区三区| 无码日韩AV一区二区三区| 国产国语高清在线视频二区| 九月婷婷亚洲综合在线| 怡红院成人在线| 欧美性xxxxx极品老少| 国产综合久久久久| 亚洲欧美日韩综合久久久久| 99国产精品免费观看视频| 狠狠色丁香婷婷久久综合| 天天综合色一区二区三区| 免费看毛片电影| www.好吊色.com| 男生和女生在一起差差的很痛| 好男人影视社区www在线观看| 又黄又爽又色的黄裸乳视频| 中文字幕av无码不卡免费| 男人强行被开发尿孔漫画| 天天干天天操天天操|