Spring學(xué)習(xí)(四)搞定切面(AOP)

Spring

導(dǎo)語(yǔ):

面向切面編程(AOP) 所要解決的問(wèn)題是把橫切關(guān)注點(diǎn)與業(yè)務(wù)邏輯相分離与涡。
DI有助于應(yīng)用對(duì)象之間的解耦挨措, 而AOP可以實(shí)現(xiàn)橫切關(guān)注點(diǎn)與它們所影響的對(duì)象之間的解耦祟同。

日志是應(yīng)用切面的常見(jiàn)范例狰住,除此之外,切面還適用聲明式事務(wù)轻纪、 安全和緩存油额。
主要內(nèi)容:

  • 面向切面編程的基本原理
  • 通過(guò)POJO創(chuàng)建切面
  • 使用@AspectJ注解
  • 為AspectJ切面注入依賴

一、面向切面編程的基本原理

橫切關(guān)注點(diǎn)可以被模塊化為特殊的類刻帚,這些類被稱為切面(aspect) 潦嘶。

  1. AOP術(shù)語(yǔ)
    通知包含了需要用于多個(gè)應(yīng)用對(duì)象的橫切行為; 連接點(diǎn)是程序執(zhí)行過(guò)程中能夠應(yīng)用通知的所有點(diǎn)崇众; 切點(diǎn)定義了通知被應(yīng)用的具體位置(在哪些連接點(diǎn)) 掂僵。 其中關(guān)鍵的概念是切點(diǎn)定義了哪些連接點(diǎn)會(huì)得到通知。
  • 通知(Advice)
    在AOP術(shù)語(yǔ)中顷歌,切面的工作被稱為通知锰蓬。通知定義了切面是什么以及何時(shí)使用。
    Spring切面可以應(yīng)用5種類型的通知:

    前置通知(Before) : 在目標(biāo)方法被調(diào)用之前調(diào)用通知功能眯漩;
    后置通知(After) : 在目標(biāo)方法完成之后調(diào)用通知芹扭, 此時(shí)不會(huì)關(guān)心方法的輸出是什么;
    返回通知(After-returning) : 在目標(biāo)方法成功執(zhí)行之后調(diào)用通知赦抖;
    異常通知(After-throwing) : 在目標(biāo)方法拋出異常后調(diào)用通知舱卡;
    環(huán)繞通知(Around) : 通知包裹了被通知的方法, 在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的行為队萤。

  • 連接點(diǎn)(Join point)(被切的類上的點(diǎn))
    連接點(diǎn)是在應(yīng)用執(zhí)行過(guò)程中能夠插入切面的一個(gè)點(diǎn)轮锥。這個(gè)點(diǎn)可以是調(diào)用方法時(shí)、 拋出異常時(shí)要尔、 甚至修改一個(gè)字段時(shí)舍杜。切面代碼可以利用這些點(diǎn)插入到應(yīng)用的正常流程之中, 并添加新的行為赵辕。
  • 切點(diǎn)(Poincut)(在切面上定義要切的類)
    切點(diǎn)有助于縮小切面所通知的連接點(diǎn)的范圍既绩。
  • 切面(Aspect)
    切面是通知和切點(diǎn)的結(jié)合。 通知和切點(diǎn)共同定義了切面的全部?jī)?nèi)容——它是什么匆帚, 在何時(shí)和何處完成其功能熬词。
  • 引入(Introduction)
    引入允許我們向現(xiàn)有的類添加新方法或?qū)傩浴?/li>
  • 織入(Weaving)
    織入是把切面應(yīng)用到目標(biāo)對(duì)象并創(chuàng)建新的代理對(duì)象的過(guò)程嘶伟。

    在目標(biāo)對(duì)象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入:
    編譯期: 切面在目標(biāo)類編譯時(shí)被織入出嘹。 這種方式需要特殊的編譯器柜裸。 AspectJ的織入編譯器就是以這種方式織入切面的桃漾。
    類加載期: 切面在目標(biāo)類加載到JVM時(shí)被織入丹莲。 這種方式需要特殊的類加載器(ClassLoader) 揖铜,它可以在目標(biāo)類被引入應(yīng)用之前增強(qiáng)該目標(biāo)類的字節(jié)碼述寡。AspectJ 5的加載時(shí)織入 (load-timeweaving找岖, LTW) 就支持以這種方式織入切面嫉晶。
    運(yùn)行期:切面在應(yīng)用運(yùn)行的某個(gè)時(shí)刻被織入骑疆。 一般情況下田篇,在織入切面時(shí), AOP容器會(huì)為目標(biāo)對(duì)象動(dòng) 態(tài)地創(chuàng)建一個(gè)代理對(duì)象箍铭。Spring AOP就是以這種方式織入切面的泊柬。

  1. Spring對(duì)AOP的支持
    創(chuàng)建切點(diǎn)來(lái)定義切面所織入的連接點(diǎn)是AOP框架的基本功能,而在SpringAOP中诈火,spring提供了4種類型的AOP支持:
  • 基于代理的經(jīng)典Spring AOP
  • 純POJO切面
  • @AspectJ注解驅(qū)動(dòng)的切面
  • 注入式AspectJ切面(適用于Spring各版本)
    前三種都是Spring AOP實(shí)現(xiàn)的變體兽赁, Spring AOP構(gòu)建在動(dòng)態(tài)代理基礎(chǔ)之上, 因此冷守, Spring對(duì)AOP的支持局限于方法攔截刀崖。

Spring AOP框架的一些關(guān)鍵知識(shí):

  • Spring通知是Java編寫的
  • Spring在運(yùn)行時(shí)通知對(duì)象
  • Spring只支持方法級(jí)別的連接點(diǎn)

二、通過(guò)切點(diǎn)來(lái)選擇連接點(diǎn)


切點(diǎn)指示器

SpringAOP中拍摇,execution指示器是我們?cè)诰帉懬悬c(diǎn)定義時(shí)最主要使用的指示器亮钦。

  1. 編寫切點(diǎn)
    要想編寫切點(diǎn),必須先創(chuàng)建一個(gè)“被切”的類充活,也叫主題:
public interface Performance {
    public void perform();
}

Performance可以代表任何類型的現(xiàn)場(chǎng)表演蜂莉, 如舞臺(tái)劇、 電影或音樂(lè)會(huì)堪唐。 假設(shè)我們想編寫Performance的perform()方法觸發(fā)的通知巡语。

我們使用execution()指示器選擇Performance的perform()方法翎蹈。 方法表達(dá)式以“*”號(hào)開(kāi)始淮菠, 表明了我們不關(guān)心方法返回值的類型。 然后荤堪, 我們指定了全限定類名和方法名合陵。 對(duì)于方法參數(shù)列表, 我
們使用兩個(gè)點(diǎn)號(hào)(..) 表明切點(diǎn)要選擇任意的perform()方法澄阳, 無(wú)論該方法的入?yún)⑹鞘裁础?/p>

切點(diǎn)表達(dá)式

現(xiàn)在假設(shè)我們需要配置的切點(diǎn)僅匹配concert包拥知。 在此場(chǎng)景下, 可以使用within()指示器來(lái)限制匹配碎赢, 如圖所示:


within()指示器

因?yàn)椤?amp;”在XML中有特殊含義低剔, 所以在Spring的XML配置里面描述切點(diǎn)時(shí), 我們可以使用and來(lái)代替“&&”肮塞。 同樣襟齿, or和not可以分別用來(lái)代替“||”和“!”。

  • 在切點(diǎn)中選擇bean
    Spring還引入了一個(gè)新的bean()指示器枕赵, 它允許我們?cè)谇悬c(diǎn)表達(dá)式中使用bean的ID來(lái)標(biāo)識(shí)bean猜欺。 bean()使用bean ID或bean名稱作為參數(shù)來(lái)限制切點(diǎn)只匹配特定的bean。
    //在執(zhí)行Performance的perform()方法時(shí)應(yīng)用通知拷窜, 但限定bean的ID為woodstock:
    execution(* concert.Performance.perform()) and bean('woodstock')
    //使用非操作為除了特定ID以外的其他bean應(yīng)用通知:
    execution(* concert.Performance.perform()) and !bean('woodstock')
    

三开皿、使用注解創(chuàng)建切面

  1. 定義切面
//以下是一個(gè)切面:
@Aspect
public class Audience {
      //先使用 @Pointcut在performance()方法上添加 @Pointcut注解涧黄,其值是一個(gè)切點(diǎn)表達(dá)式。
  @Pointcut("execution(** aopdemo.Performance.perform(..))")
  public void performance() {}
  //使用環(huán)繞通知實(shí)現(xiàn)Audience切面
  @Around("performance()")
  public void watchPerformance(ProceedingJoinPoint jp) {
      try {
          System.out.println("觀眾1");
          //System.out.println("開(kāi)場(chǎng)前通知");
          //System.out.println("入座");
          jp.proceed();
          System.out.println("觀眾1鼓掌");
      } catch (Throwable e) {
          System.out.println("發(fā)生演出事故了赋荆。笋妥。。");
      }
  }
}
  • performance()方法的實(shí)際內(nèi)容并不重要窄潭, 在這里它實(shí)際上應(yīng)該是空的挽鞠。 其實(shí)該方法本身只是一個(gè)標(biāo)識(shí), 供 @Pointcut注解依附狈孔。
  • 環(huán)繞通知是最為強(qiáng)大的通知類型信认。 它能夠讓你所編寫的邏輯將被通知的目標(biāo)方法完全包裝起來(lái)。實(shí)際上就像在一個(gè)通知方法中同時(shí)編寫前置通知和后置通知均抽。
  1. 啟用自動(dòng)代理功能
  • JavaConfig中啟用AspectJ注解的自動(dòng)代理
@Configuration
@EnableAspectJAutoProxy
public class PerformanceConfig {
    
    @Bean
    public Performance getPer() {
        return new NCYanChu();
    }
    
    @Bean
    public Audience audience() {
        return new Audience();
    }
    
    @Bean
    public Audience2 audience2() {
        return new Audience2();
    }
}
  • 在XML中嫁赏, 通過(guò)Spring的aop命名空間啟用AspectJ自動(dòng)代理


    xml啟動(dòng)aop代理
  1. 處理通知中的參數(shù)
//切面所通知的方法有參數(shù)
@Pointcut("execution(**aopdemo.Performance.perform(String))&&args(programName)")
public void performance(String programName) {}
@Around("performance(programName)")
public void watchPerformance(ProceedingJoinPoint jp,String programName) {
        try {
            System.out.println("觀眾2");
            //System.out.println("開(kāi)場(chǎng)前通知");
            //System.out.println("入座");
            jp.proceed();
            System.out.println("表演者:"+programName);
            System.out.println("觀眾2鼓掌");
        } catch (Throwable e) {
            System.out.println("發(fā)生演出事故了。油挥。潦蝇。");
        }
    }

4.通過(guò)注解引入新功能

@Aspect
public class Audience3 {
    @DeclareParents(value="aopdemo.Performance+",defaultImpl=DefaultEncoreable.class)
    public static Encoreable encoreable;
}

@DeclareParents注解由三部分組成:

  • value屬性指定了哪種類型的bean要引入該接口。 在本例中深寥, 也就是所有實(shí)現(xiàn)Performance的類型攘乒。 (標(biāo)記符后面的加號(hào)表示是Performance的所有子類型, 而不是Performance本身惋鹅。 )
  • defaultImpl屬性指定了為引入功能提供實(shí)現(xiàn)的類则酝。 在這里,我們指定的是DefaultEncoreable提供實(shí)現(xiàn)闰集。
  • @DeclareParents注解所標(biāo)注的靜態(tài)屬性指明了要引入了接
    口沽讹。 在這里, 我們所引入的是Encoreable接口武鲁。

四爽雄、在XML中聲明切面

如果需要聲明切面, 但是又不能為通知類添加注解的時(shí)候沐鼠, 那么就必須轉(zhuǎn)向XML配置了挚瘟。
優(yōu)點(diǎn):Spring的AOP配置元素能夠以非侵入性的方式聲明切面

AOP配置元素                              用 途
<aop:advisor>                           定義AOP通知器
<aop:after>                             定義AOP后置通知(不管被通知的方法是否執(zhí)行成功)
<aop:after-returning>                   定義AOP返回通知
<aop:after-throwing>                    定義AOP異常通知
<aop:around>                            定義AOP環(huán)繞通知
<aop:aspect>                            定義一個(gè)切面
<aop:aspectj-autoproxy>                 啟用 @AspectJ注解驅(qū)動(dòng)的切面
<aop:before>                            定義一個(gè)AOP前置通知
<aop:config>                            頂層的AOP配置元素。 大多數(shù)的<aop:*>元素必須包含在<aop:config>元素內(nèi)
<aop:declare-parents>                   以透明的方式為被通知的對(duì)象引入額外的接口
<aop:pointcut>                          定義一個(gè)切點(diǎn)
<?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.xsd">
  <aop:config>
    <aop:aspect ref="audience">
        <aop:pointcut 
            id="performance" 
            expression="execution(** aopdemo.Performance.perform(..))" />
        <aop:around
             pointcut-ref="performance"
             method="watchPerformance" />
    </aop:aspect>
  </aop:config>
</beans>

五饲梭、注入AspectJ切面

切面很可能依賴其他類來(lái)完成它們的工作乘盖。如果在執(zhí)行通知時(shí), 切面依賴于一個(gè)或多個(gè)類排拷,我們可以在切面內(nèi)部實(shí)例化這些協(xié)作的對(duì)象侧漓。 但更好的方式是, 我們可以借助Spring的依賴注入把bean裝配進(jìn)AspectJ切面中监氢。

  1. 定義切面:
  • java類定義
package aopdemo;
@Aspect                                   
public class CriticAspect { //這里也可以使用package aopdemo;
                                           //無(wú)@Aspect注解
                                           public aspect CriticAspect{}
    public CriticAspect() {}
    public static CriticAspect aspectOf() {
        return new CriticAspect();
    }
    @Pointcut ("execution(* perform(..))")
    public void performance() {}
    @AfterReturning("performance()")
    public void perFinish() {
        System.out.println(123);
        System.out.println(criticismEngine.getCriticism());
    }
//  Pointcut performance()  "execution(* perform(..))";
    private CriticismEngine criticismEngine;
    public void setCriticismEngine(CriticismEngine criticismEngine) {
        this.criticismEngine = criticismEngine;
    }
    public CriticismEngine getCriticismEngine() {
        return criticismEngine;
    }   
}
  • xml配置定義:
<aop:aspectj-autoproxy />
    <bean id="criticismEngine" class="aopdemo.CriticismEngineImpl">
        <property name="criticismPool">
            <list>
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </list>
        </property>
    </bean>
    <bean class="aopdemo.CriticAspect" factory-method="aspectOf">
        <property name="criticismEngine" ref="criticismEngine"></property>
    </bean>
    <bean class="aopdemo.NCYanChu">
    </bean>
 //其中aspectOf方法是CriticAspect類中生成CriticAspect實(shí)例的靜態(tài)工廠方法:
 public static CriticAspect aspectOf() {
    return new CriticAspect();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末布蔗,一起剝皮案震驚了整個(gè)濱河市藤违,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纵揍,老刑警劉巖顿乒,帶你破解...
    沈念sama閱讀 211,496評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異泽谨,居然都是意外死亡璧榄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門吧雹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)骨杂,“玉大人,你說(shuō)我怎么就攤上這事雄卷〈牝剑” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 157,091評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵丁鹉,是天一觀的道長(zhǎng)妒潭。 經(jīng)常有香客問(wèn)我,道長(zhǎng)揣钦,這世上最難降的妖魔是什么雳灾? 我笑而不...
    開(kāi)封第一講書人閱讀 56,458評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮冯凹,結(jié)果婚禮上谎亩,老公的妹妹穿的比我還像新娘。我一直安慰自己谈竿,他們只是感情好团驱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,542評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著空凸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寸痢。 梳的紋絲不亂的頭發(fā)上呀洲,一...
    開(kāi)封第一講書人閱讀 49,802評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音啼止,去河邊找鬼道逗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛献烦,可吹牛的內(nèi)容都是我干的滓窍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,945評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼巩那,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吏夯!你這毒婦竟也來(lái)了此蜈?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,709評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤噪生,失蹤者是張志新(化名)和其女友劉穎裆赵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跺嗽,經(jīng)...
    沈念sama閱讀 44,158評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡战授,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,502評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桨嫁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片植兰。...
    茶點(diǎn)故事閱讀 38,637評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖璃吧,靈堂內(nèi)的尸體忽然破棺而出钉跷,到底是詐尸還是另有隱情,我是刑警寧澤肚逸,帶...
    沈念sama閱讀 34,300評(píng)論 4 329
  • 正文 年R本政府宣布爷辙,位于F島的核電站,受9級(jí)特大地震影響朦促,放射性物質(zhì)發(fā)生泄漏膝晾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,911評(píng)論 3 313
  • 文/蒙蒙 一务冕、第九天 我趴在偏房一處隱蔽的房頂上張望血当。 院中可真熱鬧,春花似錦禀忆、人聲如沸臊旭。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,744評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)离熏。三九已至,卻和暖如春戴涝,著一層夾襖步出監(jiān)牢的瞬間滋戳,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,982評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工啥刻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奸鸯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,344評(píng)論 2 360
  • 正文 我出身青樓可帽,卻偏偏與公主長(zhǎng)得像娄涩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子映跟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,500評(píng)論 2 348

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

  • 本章內(nèi)容: 面向切面編程的基本原理 通過(guò)POJO創(chuàng)建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,132評(píng)論 0 9
  • 在生活中,監(jiān)控用電量是一個(gè)很重要的功能弯蚜,但并不是大多數(shù)家庭重點(diǎn)關(guān)注的問(wèn)題孔轴。軟件系統(tǒng)的一些功能就像家里的電表一樣,這...
    yjaal閱讀 569評(píng)論 0 3
  • 前幾天公司開(kāi)半年總結(jié)會(huì)碎捺。開(kāi)到下午的時(shí)候我特別困路鹰,一直打瞌睡。 看著公司滿懷激情與希望的說(shuō)愿景收厨,談夢(mèng)想晋柱,表彰先進(jìn)個(gè)人...
    末行閱讀 288評(píng)論 1 1
  • 在2004年以前中國(guó)白領(lǐng)的工資相對(duì)于房?jī)r(jià)有很強(qiáng)的優(yōu)勢(shì)雁竞,毫不客氣的說(shuō),工作可以當(dāng)事業(yè)來(lái)做拧额,工資的購(gòu)買力確實(shí)很強(qiáng)碑诉。 我...
    西瓜like夏天閱讀 158評(píng)論 0 0