(FS計劃)理清楚Spring的AOP到底怎么玩


前面簡單介紹了Spring中AOP的使用,是基于XML配置。這次詳細介紹一下Spring中AOP的使用和實現(xiàn)。

0x00 回顧

package com.zing.aspect_oriented_test;
/**
 * 方法切面
 */
public class SubjectService implements InterfaceSubjectService {
    @Override
    public void AspectMethod(String str) {
        System.out.println("yo yo yo!\t" + str);
    }
}
package com.zing.aspect_oriented_test;
/**
 * 對切面的處理,之前與之后
 */public class AspectTarget {
    public void beforeYouTalk() {
        System.out.println("************beforeYouTalk, I know everything,so do not lie!");
    }
    private void afterYouTalk() {
        System.out.println("************afterYouTalk, ha ha ha,good boy");
    }
}

我們知道了切面和切面處理方法舵稠,接下來告訴Spring,切面位置和處理方法
在XML配置文件中將兩個bean配置到IoC容器中

<bean id="aspectService" class="com.zing.aspect_oriented_test.SubjectService"></bean>
<bean id="aspect" class="com.zing.aspect_oriented_test.AspectTarget"></bean>

再配置切面和切面方法

<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.zing...*.*(..))"></aop:pointcut>
    <aop:aspect ref="aspect">
        <aop:before pointcut-ref="pointCut" method="beforeYouTalk"></aop:before>
        <aop:after pointcut="execution(* com.zing..*.*(..))"  method="afterYouTalk"></aop:after>
    </aop:aspect>
</aop:config>

上面的配置可能不太明白,因為用的是AspectJ語法,* com.zing..*.*(..)表示com.zing包下的所有子包的類與方法

例子擼完驰弄,寫個Junit測試一下

package com.zing.aspect_oriented_test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * Created by zing on 16/4/23.
 */
public class AspectJunitTest {
    @Test
    public void AopTest(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        InterfaceSubjectService subjectService = applicationContext.getBean("aspectService",InterfaceSubjectService.class);
        subjectService.AspectMethod("Monster");
    }
}

想必看完云里霧里的冕广,這里面用到了AOP的配置,完全沒有概念盈滴,接下來詳細解釋一下涯肩,配置的含義

0x01 Aspect切面

Aspect,是織入的節(jié)點巢钓,前面兩篇文章已經(jīng)介紹了代理病苗,而代理的節(jié)點,就是用Aspect來標記的症汹,因為Spring封裝了優(yōu)秀的AspectJ解決方案硫朦,Aspect作為一個既定的接口,被Spring擴展了多個具體類型的通知BeforeAdvice,AfterAdvice,ThrowsAdvice

BeforeAdvice中分析背镇,Spring中將前置接口設(shè)定為MethodBeforeAdvice咬展,這個接口中只需要實現(xiàn)before方法

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method var1, Object[] var2, Object var3) throws Throwable;
}

這個方法將會在目標方法執(zhí)行前被回調(diào)泽裳。

同樣AfterAdvice是后置通知,具體的繼承有AfterReturningAdvice

public interface AfterReturningAdvice extends AfterAdvice {
    void afterReturning(Object var1, Method var2, Object[] var3, Object var4) throws Throwable;
}

實現(xiàn)這個接口的afterReturning方法破婆,在目標方法成功執(zhí)行并返回值之后涮总,AOP會回調(diào)afterReturning方法

最后ThrowsAdvice,其實是AfterAdvice子接口祷舀,在目標方法執(zhí)行發(fā)生異常時瀑梗,會被回調(diào)。
具體可以這么實現(xiàn)


class BoomException implements ThrowsAdvice{
    public void afterThrowing(IOException ioEx){
        System.out.println("IO異常:"+ioEx.getMessage());
    }

    public void afterThrowing(ClassCastException ccEx){
        System.out.println("轉(zhuǎn)換異常:"+ccEx.getMessage());
    }
}

0x02 Pointcut切點

切點定義了一個代理接入位置裳扯,決定了通知作用的連接點抛丽。當然,也可以是一堆連接點嚎朽,一般用一個正則表達式標識铺纽。

public interface Pointcut {
    Pointcut TRUE = TruePointcut.INSTANCE;

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();
}

其中getMethodMatcher()就是獲取一個方法過濾器,這個方法過濾器將符合標準的方法哟忍,作為切面的連接點狡门。
關(guān)于MethodMatcher,可以查看一下Spring源碼锅很。這里不做過多解釋其馏。

0x03 Advisor通知器

配置中并沒有寫Advisor,所以簡單介紹一下爆安,一個完整的模塊叛复,當要進行AOP編程時,需要將方法標記為切面扔仓,并定義了切面前置通知褐奥、后置通知、異常通知翘簇。定義完成撬码,需要通過通知器,將切面和通知綁定起來版保,這個通知器就是Advisor呜笑。

Advisor將Advice和Pointcut結(jié)合起來,通過IoC容器來配置AOP來使用彻犁。

0x04 ProxyFactoryBean

ProxyFactoryBean是Spring利用Java的代理模式或者CGLIB來實現(xiàn)Aop的一種方式叫胁,如何在XML中配置ProxyFactoryBean?

  • 通知器Advisor使用Bean來配置汞幢。
  • 織入方法類使用Bean配置
  • 定義ProxyFactoryBean驼鹅,為這個bean配置幾個參數(shù):
    • 目標:target
    • 代理接口:proxyInterface
    • 織入類:interceptName

如果不清楚可以看AOP和Spring中AOP的簡單介紹中的第0x03小節(jié)的例子。這里就扣下代碼,里面還有配置后置方法織入谤民,異常方法通知織入堰酿,不一一介紹。

  <bean id="aspectService" class="com.zing.aspect_oriented_test.SubjectService"></bean>
  <bean id="aspect" class="com.zing.aspect_oriented_test.AspectTarget"></bean>

  <bean id="rocketProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="launchingControl"></property>
        <property name="proxyInterfaces" value="com.zing.aoptest.IRocketLaunching"></property>
        <property name="interceptorNames" value="beforeLaunch"></property>
        <property name="proxyTargetClass" value="true"></property>
    </bean>

因為ProxyFactoryBean是依靠Java或CGLIB的Proxy方式來獲取對象的张足,使用依靠代理的getObject()方法來作為入口触创。使用接下來看一下這個方法的實現(xiàn)方式

public Object getObject() throws BeansException {
//初始化通知器
        this.initializeAdvisorChain();
//區(qū)分單例模式和原始模式prototype
        if(this.isSingleton()) {
            return this.getSingletonInstance();
        } else {
            if(this.targetName == null) {
                this.logger.warn("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the \'targetName\' property.");
            }
            return this.newPrototypeInstance();
        }
    }

具體可以追一追newPrototypeInstance()getSingletonInstance()兩個方法,得到實現(xiàn)方式的完整過程为牍。留給感興趣的小伙伴哼绑。因為再往里挖就挖到CGLIB和JDK對象生成里去了,感覺刨過頭了碉咆。

0x05 Schema的AOP配置

前面啰嗦了一大堆抖韩,我覺得應(yīng)該介紹一下具體的配置方式

 <!--aop定義開始-->
    <aop:config>
        <!--定義通知器-->
        <aop:advisor ref="aspectSupportBean"></aop:advisor>
        <!--定義切面 ref表示引用的bean-->
        <aop:aspect ref="aspectSupportBean">
            <!--定義切面增強位置-->
            <aop:pointcut id="pcut" expression="execution(* cn.javass..*.*(..))" ></aop:pointcut>
            <!--前置通知,下面的參數(shù)跟第一個類似-->
            <aop:before pointcut="切入點表達式" pointcut-ref="切入點Bean引用"  method="前置通知實現(xiàn)方法名" arg-names="前置通知實現(xiàn)方法參數(shù)列表參數(shù)名字"/>
            <!--后置返回通知-->
            <aop:after-returning></aop:after-returning>
            <!--異常通知-->
            <aop:after-throwing></aop:after-throwing>
            <!--最終通知-->
            <aop:after></aop:after>
            <!--環(huán)繞通知-->
            <aop:around></aop:around>
            <!--引入定義-->
            <aop:declare-parents  types-matching="AspectJ語法類型表達式" implement-interface=引入的接口"  default-impl="引入接口的默認實現(xiàn)"  delegate-ref="引入接口的默認實現(xiàn)Bean引用"/>
        </aop:aspect>
    </aop:config>

如果你看完前面的東西應(yīng)該不難理解這些配置
但是有一個execution疫铜,為此查閱了一下文檔茂浮,切入點指示符。執(zhí)行表達式的格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

除了返回類型模式(上面代碼片斷中的ret-type-pattern)壳咕,名字模式和參數(shù)模式以外席揽, 所有的部分都是可選的。返回類型模式?jīng)Q定了方法的返回類型必須依次匹配一個連接點谓厘。 你會使用的最頻繁的返回類型模式是*幌羞,它代表了匹配任意的返回類型。 一個全限定的類型名將只會匹配返回給定類型的方法竟稳。名字模式匹配的是方法名属桦。
你可以使用*通配符作為所有或者部分命名模式。 參數(shù)模式稍微有點復(fù)雜:()

匹配了一個不接受任何參數(shù)的方法他爸, 而(..)

匹配了一個接受任意數(shù)量參數(shù)的方法(零或者更多)聂宾。 模式(*)

匹配了一個接受一個任何類型的參數(shù)的方法。 模式(*,String)

匹配了一個接受兩個參數(shù)的方法诊笤,第一個可以是任意類型亏吝, 第二個則必須是String類型。

0x06 @AspectJ的AOP

是基于注解的AOP盏混,默認Spring是不開啟的,需要再XML里添加一行配置

<aop:aspectj-autoproxy/>

之后便可以用注解的方式使用AOP了,我列舉一下

//配置切面
@Aspect() 
Public class Aspect{ 
…… 
}
//配置織入方法
@Pointcut(value="切入點表達式", argNames = "參數(shù)名列表") 
public void pointcutName(……) {}
//前置通知
@Before(value = "切入點表達式或命名切入點", argNames = "參數(shù)列表參數(shù)名")

//后置返回通知
@AfterReturning( 
value="切入點表達式或命名切入點", 
pointcut="切入點表達式或命名切入點", 
argNames="參數(shù)列表參數(shù)名", 
returning="返回值對應(yīng)參數(shù)名") 
// value:指定切入點表達式或命名切入點惜论;
// pointcut:同樣是指定切入點表達式或命名切入點许赃,如果指定了將覆蓋value屬性指定的,pointcut具有高優(yōu)先級馆类;
// argNames:與Schema方式配置中的同義混聊;
// returning:與Schema方式配置中的同義。

//后置通知
@After ( 
value="切入點表達式或命名切入點", 
argNames="參數(shù)列表參數(shù)名") 
//value:指定切入點表達式或命名切入點乾巧;
// argNames:與Schema方式配置中的同義句喜;


//異常通知
@AfterThrowing ( 
value="切入點表達式或命名切入點", 
pointcut="切入點表達式或命名切入點", 
argNames="參數(shù)列表參數(shù)名", 
throwing="異常對應(yīng)參數(shù)名")

//環(huán)繞通知
@Around ( 
value="切入點表達式或命名切入點", 
argNames="參數(shù)列表參數(shù)名")

//引用
@DeclareParents( 
value=" AspectJ語法類型表達式", 
defaultImpl=引入接口的默認實現(xiàn)類) 
private Interface MyInterface;

只是簡化了配置预愤,用起來跟Schema類似

參考

http://www.importnew.com/17795.html
http://www.importnew.com/17813.html
《Spring 內(nèi)幕技術(shù)》

轉(zhuǎn)載請注明出處:理清楚Spring的AOP到底怎么玩


FS全棧計劃目錄:https://micorochio.github.io/fs-plan/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咳胃,隨后出現(xiàn)的幾起案子植康,更是在濱河造成了極大的恐慌,老刑警劉巖展懈,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件销睁,死亡現(xiàn)場離奇詭異,居然都是意外死亡存崖,警方通過查閱死者的電腦和手機冻记,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來来惧,“玉大人冗栗,你說我怎么就攤上這事」┎螅” “怎么了隅居?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趁曼。 經(jīng)常有香客問我军浆,道長,這世上最難降的妖魔是什么挡闰? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任乒融,我火速辦了婚禮,結(jié)果婚禮上摄悯,老公的妹妹穿的比我還像新娘赞季。我一直安慰自己,他們只是感情好奢驯,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布申钩。 她就那樣靜靜地躺著,像睡著了一般瘪阁。 火紅的嫁衣襯著肌膚如雪撒遣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天管跺,我揣著相機與錄音义黎,去河邊找鬼。 笑死豁跑,一個胖子當著我的面吹牛廉涕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼狐蜕,長吁一口氣:“原來是場噩夢啊……” “哼宠纯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起层释,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤婆瓜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后湃累,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勃救,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年治力,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒙秒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡宵统,死狀恐怖晕讲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情马澈,我是刑警寧澤瓢省,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站痊班,受9級特大地震影響勤婚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涤伐,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一馒胆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凝果,春花似錦祝迂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至山害,卻和暖如春纠俭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浪慌。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工柑晒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人眷射。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妖碉。 傳聞我的和親對象是個殘疾皇子涌庭,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)欧宜,斷路器坐榆,智...
    卡卡羅2017閱讀 134,701評論 18 139
  • 本博中關(guān)于spring的文章:Spring IOC和AOP原理,Spring事務(wù)原理探究冗茸,Spring配置文件屬性...
    Maggie編程去閱讀 4,106評論 0 34
  • title: Spring_AOP源碼分析date: 2016-11-03 01:15:11categories:...
    raincoffee閱讀 1,757評論 2 36
  • 對于常用標簽div和span 就不多說了席镀,在我們開始接觸HTML5的時候最先了解的就應(yīng)該是他們了。div和span...
    經(jīng)典式微笑閱讀 394評論 1 1
  • 盛唐的月 大秦的雨 千年的禪寺 佛經(jīng)里的茶 幾章回的英雄 還有夜里的銀河倒瀉 以及淋濕的驚慌失措的你
    艾特五百里閱讀 161評論 0 0