spring高質(zhì)量系列-AOP

spring支持的AOP的方式有:AspecJ,ProxyFactoryBean,ProxyFactory
其中:AspectJ是目前大家最常用的 起到集成AspectJ和Spring玄货,ProxyFactoryBean是將我們的AOP和IOC融合起來,而ProxyFactory 則是只能通過代碼硬編碼進(jìn)行編寫 一般都是給spring自己使用

AnnotationAwareAspectJAutoProxyCreator 通過繼承SmartInstantiationAwareBeanPostProcessor 來將AsepctJ融入spring 內(nèi)部生成的代理對象是采用ProxyFactory

涉及到的幾個關(guān)鍵類

ProxyConfig:為上面三個類提供配置屬性
AdvisedSupport:繼承ProxyConfig,實現(xiàn)了Advised羔砾。封裝了對通知(Advise)和通知器(Advisor)的操作
ProxyCreatorSupport:繼承AdvisedSupport砾医,其幫助子類(上面三個類)創(chuàng)建JDK或者cglib的代理對象
Advised:可以獲取攔截器和其他 advice, Advisors和代理接口

涉及到的幾個關(guān)鍵概念

Advice:通知带污,定義在連接點做什么,比如我們在方法前后進(jìn)行日志打印
pointcut:切點宛琅,決定advice應(yīng)該作用于那個連接點脾歇,比如根據(jù)正則等規(guī)則匹配哪些方法需要增強
Pointcut 目前有g(shù)etClassFilter(類匹配)臀晃,getMethodMatcher(方法匹配),Pointcut TRUE (全匹配)
JoinPoint:連接點介劫,就是spring允許你是通知(Advice)的地方徽惋,那可就真多了,基本每個方法的錢座韵、后(兩者都有也行)险绘,或拋出異常是時都可以是連接點,spring只支持方法連接點誉碴。其他如AspectJ還可以讓你在構(gòu)造器或?qū)傩宰⑷霑r都行宦棺,不過那不是咱們關(guān)注的,只要記住黔帕,和方法有關(guān)的前前后后都是連接點代咸。
advisor:把pointcut和advice連接起來

ProxyFactory

 public static void main(String[] args) {
        MarshallService marshallService = new MarshallService();
        ProxyFactory proxyFactory = new ProxyFactory(marshallService);
        proxyFactory.addAdvice(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("MethodBeforeAdvice1 method=" + method.getName());
            }
        });


        proxyFactory.addAdvice(new MarshallAdvice());
        MarshallService proxy = (MarshallService) proxyFactory.getProxy();
        proxy.say1();
        proxy.say2();
    }

1.ProxyFactory proxyFactory = new ProxyFactory(marshallService); 分別設(shè)置AdvisedSupport類的targetSource,interfaces成黄,并且清空methodCache

targetSource:我們需要代理的原始對象呐芥,interfaces:該原始對象實現(xiàn)的接口,methodCache:保存某個方法對應(yīng)的advisor chain List
3.proxyFactory.addAdvice(new MarshallAdvice()); 添加advice奋岁,先判斷advice的類型思瘟,然后將其包裝成不同的advisor,一般都是默認(rèn)的DefaultPointcutAdvisor闻伶,然添加進(jìn)入集合advisors滨攻,并且更新相應(yīng)的數(shù)組advisorArray(為了方便操作)
4 . MarshallService proxy = (MarshallService) proxyFactory.getProxy(); 獲取代理對象
5 . 首先調(diào)用了父類ProxyCreatorSupport的createAopProxy去激活(即激活相關(guān)的監(jiān)聽事件),然后獲取AopProxyFactory,通過這factory獲取AopProxy(ObjenesisCglibAopProxy光绕,JdkDynamicAopProxy)女嘲,通過AopProxy獲取真正的代理對象

cglib

6.AopProxy.getProxy()方法對于cglib主要是設(shè)置enhancer的Callback,這些callback會在代理對象執(zhí)行方法時候調(diào)用诞帐,callback的類型有
MethodInterceptor:這就是我們aop欣尼,因為 MethodInterceptor的效率不高,它需要產(chǎn)生不同類型的字節(jié)碼景埃,并且需要生成一些運行時對象(InvocationHandler就不需要),所以Cglib提供了其它的接口供我們選擇
NoOp:簡單地把方法調(diào)用委托給了被代理類的原方法(本例中是Person)顶别,不做任何其它的操作
LazyLoader:谷徙,它也提供了一個方法:Object loadObject() throws Exception;,loadObject()方法會在第一次被代理類的方法調(diào)用時觸發(fā)驯绎,它返回一個代理類的對象完慧,這個對象會被存儲起來然后負(fù)責(zé)所有被代理類方法的調(diào)用
Dispatcher :也是提供了loadObject()方法,這個方法同樣地返回一個代理對象剩失,這個對象同樣可以代理原方法的調(diào)用屈尼。不過它們之間不同的地方在于,Dispatcher的loadObject()方法在每次發(fā)生對原方法的調(diào)用時都會被調(diào)用并返回一個代理對象來調(diào)用原方法拴孤。也就是說Dispatcher的loadObject()方法返回的對象并不會被存儲起來脾歧,可以類比成Spring中的Prototype類型,而LazyLoader則是lazy模式的Singleton
InvocationHandler:它的使用方式和MethodInterceptor差不多演熟,所有對invoke()方法的參數(shù)proxy對象的方法調(diào)用都會被委托給同一個InvocationHandler鞭执,所以可能會導(dǎo)致無限循環(huán)
FixedValue:同樣也提供了一個loadObject()方法,不過這個方法返回的不是代理對象芒粹,而是原方法調(diào)用想要的結(jié)果兄纺。也就是說,在這個Callback里面化漆,看不到任何原方法的信息估脆,也就沒有調(diào)用原方法的邏輯,不管原方法是什么都只會調(diào)用loadObject()并返回一個結(jié)果座云。聽起來可能有些沒有疙赠,但是配合CllbackFilter可以強制使某個方法返回固定的值,并且?guī)淼拈_銷很小朦拖。需要注意的是棺聊,如果loadObject()方法的返回值并不能轉(zhuǎn)換成原方法的返回值類型,那么會拋出類型轉(zhuǎn)換異常贞谓。

jdk

  1. 除了生成字節(jié)碼不一樣其他的都類似 都是調(diào)用方法時候被攔截然后通過ProxyCreatorSupport類getInterceptorsAndDynamicInterceptionAdvice獲取每個方法對應(yīng)的advice
    8.spring 的AspectJ代理好像就是使用ProxyFactory去生成代理

ProxyFactoryBean

具體代碼如下

    MarshallProxyFactoryBean marshallProxyFactoryBean = (MarshallProxyFactoryBean)applicationContext.getBean("&MarshallProxyFactoryBean");
下面三個屬性 也可以通過xml配置
        //advic
        marshallProxyFactoryBean.setInterceptorNames(new String[]{"MarshallAdvice"});
        //代理類實現(xiàn)的接口限佩,如果沒有就是cglib
        marshallProxyFactoryBean.setProxyInterfaces(new Class[]{MarshallInterface.class});
        //需要生成proxy的對象
        marshallProxyFactoryBean.setTarget(new MarshallService());
具體生成proxy
        marshallProxyFactoryBean.getObject();
  1. getObject 就是通過initializeAdvisorChain初始化advisor
    2.通過advisorAdapterRegistry 包裝咱們的advice,邏輯是如果本身就是advisor 直接返回,如果是MethodInterceptor祟同,包裝成DefaultPointcutAdvisor(攔截所有方法)作喘,看是否是屬于AdvisorAdapter(MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter晕城,ThrowsAdviceAdapter)泞坦,也包裝成,包裝成DefaultPointcutAdvisor
    3.最終都是調(diào)用CglibAopProxy或者JdkDynamicAopProxy 獲取動態(tài)代理對象

AspectJProxyFactory

就是通過@AspectJ 獲取對應(yīng)的class 然后從class里面獲取Pointcut 即獲取匹配哪些類的哪些方法的規(guī)則砖顷,然后在獲取各個advice 組裝起來
最終通過getProxy獲取代理對象 通過父類統(tǒng)一初始化advisor和method的映射關(guān)系

總結(jié) AspectJProxyFactory,ProxyFactoryBean,ProxyFactory 大體邏輯都是填充AdvisedSupport(ProxyCreatorSupport是其子類)的贰锁,然后交給父類ProxyCreatorSupport,然后得到JDK或者CGLIB的AopProxy滤蝠,代理調(diào)用時候被invoke或者intercept方法攔截 (分別在JdkDynamicAopProxy和ObjenesisCglibAopProxy的中)并且在這兩個方法中調(diào)用ProxyCreatorSupport的getInterceptorsAndDynamicInterceptionAdvice方法去初始化advice和各個方法直接映射關(guān)系并緩存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豌熄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子物咳,更是在濱河造成了極大的恐慌锣险,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件览闰,死亡現(xiàn)場離奇詭異芯肤,居然都是意外死亡,警方通過查閱死者的電腦和手機压鉴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門崖咨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人油吭,你說我怎么就攤上這事掩幢。” “怎么了上鞠?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵际邻,是天一觀的道長。 經(jīng)常有香客問我芍阎,道長世曾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任谴咸,我火速辦了婚禮轮听,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘岭佳。我一直安慰自己血巍,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布珊随。 她就那樣靜靜地躺著述寡,像睡著了一般柿隙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲫凶,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天禀崖,我揣著相機與錄音,去河邊找鬼螟炫。 笑死波附,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昼钻。 我是一名探鬼主播掸屡,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼然评!你這毒婦竟也來了仅财?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤沾瓦,失蹤者是張志新(化名)和其女友劉穎满着,沒想到半個月后谦炒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贯莺,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年宁改,在試婚紗的時候發(fā)現(xiàn)自己被綠了缕探。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡还蹲,死狀恐怖爹耗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谜喊,我是刑警寧澤潭兽,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站斗遏,受9級特大地震影響山卦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诵次,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一账蓉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逾一,春花似錦铸本、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怨规。三九已至,卻和暖如春汪茧,著一層夾襖步出監(jiān)牢的瞬間椅亚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工舱污, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呀舔,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓扩灯,卻偏偏與公主長得像媚赖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子珠插,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

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