第三章 AOP 面向切面的編程

3.1 增加功能,導(dǎo)致的一些問題

1皱碘、源代碼中、業(yè)務(wù)方法中增加的功能
1)源代碼改動(dòng)的比較多
2)重復(fù)代碼比較多
3)代碼難以維護(hù)

3.2 AOP概念

3.2.1 什么是AOP

AOP(Aspect Orient Programming):面向切面編程

  • Aspect:表示切面,給業(yè)務(wù)方法增加功能斋陪,叫做切面。切面一般都是非業(yè)務(wù)功能置吓,而且切面功能一般都是可以復(fù)用的无虚。例如:日志、事務(wù)衍锚、權(quán)限檢查友题、參數(shù)檢查、統(tǒng)計(jì)信息等
  • Orient:面向戴质、對(duì)著
  • Programming:編程
    怎么理解面向切面編程度宦?(以切面為核心,設(shè)計(jì)開發(fā)你的應(yīng)用)
  • 設(shè)計(jì)項(xiàng)目的時(shí)候告匠,找出切面的功能戈抄。
  • 安排切面的執(zhí)行時(shí)間、位置

3.2.2 AOP的作用

1)讓切面功能復(fù)用
2)讓開發(fā)人員專注于業(yè)務(wù)邏輯后专、提高開發(fā)效率
3)實(shí)現(xiàn)業(yè)務(wù)功能和非業(yè)務(wù)功能解耦合
4)給存在的業(yè)務(wù)方法增加功能划鸽,不用修改原來的代碼

3.2.3 AOP中的一些術(shù)語

1) Aspect:切面,給業(yè)務(wù)方法增加的功能
2)JoinPoint:連接點(diǎn)行贪,連接切面的業(yè)務(wù)方法漾稀,在這個(gè)業(yè)務(wù)方法執(zhí)行時(shí)模闲,同時(shí)執(zhí)行這個(gè)切面的功能
3)PointCut:是一個(gè)或多個(gè)連接點(diǎn)的集合,表示這些方法執(zhí)行時(shí)崭捍,都能去增加切面的功能尸折。表示切面執(zhí)行的位置
4)target:目標(biāo)對(duì)象,給哪個(gè)對(duì)象增加切面方法殷蛇,這個(gè)對(duì)象就是目標(biāo)對(duì)象
5)Advice:通知(IT術(shù)語:增強(qiáng))实夹,表示切面的執(zhí)行時(shí)間,在目標(biāo)方法之前執(zhí)行切面粒梦,還是在目標(biāo)方法之后執(zhí)行亮航。
AOP中三個(gè)重要的要素:
Aspect、PointCut匀们、Advice缴淋。這個(gè)概念的理解是:在Advice的時(shí)間,在PointCut的位置執(zhí)行Aspect
AOP是一個(gè)動(dòng)態(tài)的思想泄朴,在程序執(zhí)行期間重抖,創(chuàng)建代理(ServiceProxy),使用代理執(zhí)行方法時(shí)祖灰,增加切面的功能钟沛,這個(gè)代理對(duì)象時(shí)存在內(nèi)存中的

3.2.4 什么時(shí)候用AOP

你要給某些方法,增加一些相同的功能局扶,源代碼不能改恨统,給業(yè)務(wù)方法增加非業(yè)務(wù)功能。

3.2.5 AOP技術(shù)思想的實(shí)現(xiàn)

使用框架實(shí)現(xiàn)AOP三妈,實(shí)現(xiàn)AOP的框架很多畜埋,比較著名的有兩個(gè):
1)Spring:Spring框架實(shí)現(xiàn)AOP思想中的部分功能,Spring框架實(shí)現(xiàn)AOP的操作比較繁瑣沈跨、比較重
2)AspectJ:一個(gè)獨(dú)立的框架由捎,專門做AOP的。屬于Eclipse基金會(huì)的

3.3 使用AspectJ框架實(shí)現(xiàn)AOP

Aspectj框架可以使用注解和xml配置文件兩種方式實(shí)現(xiàn)AOP

3.3.1 通知

Aspectj表示起么的執(zhí)行時(shí)間饿凛,用的通知(Advice)狞玛。這個(gè)通知可以用注解表示.
講5個(gè)注解,表示切面的5個(gè)執(zhí)行時(shí)間涧窒,這些注解叫做通知注解
@Before:前置通知
@AfterReturning:后置通知
@Aroud:環(huán)繞通知
@AfterThrowing:異常通知
@After:最終通知

3.3.2 PointCut 位置

PointCut用來表示切面執(zhí)行的位置心肪,使用AspectJ中切入點(diǎn)表達(dá)式
切入點(diǎn)表達(dá)式語法:execution(訪問權(quán)限 方法的返回值 方法聲明(參數(shù)) 異常類型)

3.3.3 @Before 前置通知

 /**
     * 切面類中的通知方法,是可以有參數(shù)的
     * JointPoint必須是他
     * JointPoint:表示正在執(zhí)行的業(yè)務(wù)方法纠吴,相當(dāng)于反射中的Method
     *      使用要求:必須是參數(shù)列表中的第一個(gè)
     *      作用:獲取方法執(zhí)行時(shí)的信息硬鞍,例如:方法的名稱、參數(shù)集合(如果想要在切面類中,獲取方法的信息固该,則需要加這個(gè))
     */
    @Before(value = "execution(* *..SomeServiceImpl.do*(..) )" )
    public void  myBefore2(JoinPoint joinPoint){
        //獲取方法的定義
        System.out.println("前置通知中锅减,獲取目標(biāo)方法的定義:"+joinPoint);
        System.out.println("前置通知中,獲取目標(biāo)方法的名稱:"+joinPoint.getSignature().getName());

        Object args[] = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println("獲取方法執(zhí)行時(shí)候的參數(shù):"+arg);
        }


        String methodName = joinPoint.getSignature().getName();
        if ("doSome".equals(methodName)){
            System.out.println("doSome=========");
        }else if ("doOther".equals(methodName)){
            System.out.println("doOther========");
        }

    }

3.3.4 后置通知@AfterReturning

@AfterReturning:在 目標(biāo)方法之后執(zhí)行

 /**
     * 后置通知方法的定義:
     *  1)方法是public的
     *  2)方法是void
     *  3)方法名稱自定義
     *  4)方法有參數(shù)伐坏,推薦使用object類型
     */
    /**
     * @AfterReturning:后置通知
     *      屬性:
     *          value:切入點(diǎn)表達(dá)式
     *          returning:自定義的變量怔匣,表示目標(biāo)方法的返回值
     *                     自定義變量名稱必須和通知方法的形參名一樣
     *      特點(diǎn):
     *      1、在目標(biāo)方法之后執(zhí)行
     *      2桦沉、能獲取到目標(biāo)方法的執(zhí)行結(jié)果
     *      3每瞒、不會(huì)影響目標(biāo)方法的執(zhí)行
     *  方法的參數(shù):
     *  Object res:表示目標(biāo)方法的返回值,使用res接收方法的調(diào)用結(jié)果
     *
     *  后置通知的執(zhí)行順序
     *  Object res = SomeServiceImpl.doOther(..);
     *  myAfterReturning(res)
     *
     * 方法返回的是String纯露、Integer剿骨、Long等基本類型,在后置通知中埠褪,修改返回浓利,是不會(huì)影響目標(biāo)方法的最后調(diào)用結(jié)果的
     * 如果返回的是對(duì)象類型,例如:Student,在后置方法中钞速,修改這個(gè)Student對(duì)象的屬性值荞膘,會(huì)影響最后的調(diào)用結(jié)果
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.do*(..))",returning = "res")
    public void myAfterReturning(JoinPoint jp,Object res){
        System.out.println("后置通知,在目標(biāo)方法之后執(zhí)行的玉工,能拿到執(zhí)行結(jié)果:"+res);
        //Object res有什么用,可以根據(jù)返回值得不同淘菩,做不同的增強(qiáng)功能
        if (null != res){
            res = "Hello";
        }
        System.out.println("后置通知遵班,在目標(biāo)方法之后執(zhí)行的,拿到執(zhí)行的結(jié)果:"+res);
    }

3.3.5 @Around環(huán)繞通知

@Around(value="切入點(diǎn)表達(dá)式")
使用環(huán)繞通知時(shí)潮改,就是調(diào)用我們切面類中的通知方法

package com.wh.ch09aspectaround.handle;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

/**
 * @Author :wanghui
 * @Date 2022/1/2 - 9:35
 */
@Aspect
public class MyAspect {
    /**
     * 煥榮通知設(shè)置:
     * 1狭郑、方法是public
     * 2、方法必須有返回值汇在,推薦使用Object類型
     * 3翰萨、方法名稱自定義
     * 4、方法必須有ProceedingJoinPoint參數(shù)
     */
    /**
     * @Around:環(huán)繞通知
     * 屬性:
     *  value:切入點(diǎn)表達(dá)式
     *  位置:方法定義的上面
     *返回值:Object:表示調(diào)用目標(biāo)方法糕殉,期望的執(zhí)行結(jié)果亩鬼,但是這個(gè)結(jié)果不一定是目標(biāo)方法自己的返回值
     *參數(shù):ProceedingJoinPoint,相當(dāng)于反射中的method
     *     作用:執(zhí)行目標(biāo)方法的,等于Method.invoke()
     * 特點(diǎn):
     * 1阿蝶、在目標(biāo)方法的前和后雳锋,都能增強(qiáng)功能
     * 2、控制目標(biāo)方法是否執(zhí)行
     * 3羡洁、可以修改目標(biāo)方法的執(zhí)行結(jié)果
     *
     */
    @Around("execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAroud(ProceedingJoinPoint pjp) throws Throwable {

        //執(zhí)行目標(biāo)方法
        Object methodReturn = null;
        System.out.println("執(zhí)行了環(huán)繞通知的MyAround方法,在目標(biāo)方法之前玷过,輸出日志時(shí)間==="+new Date());
        methodReturn =   pjp.proceed();//method invoke,表示執(zhí)行doFirst()方法本身
        System.out.println("環(huán)繞通知,在目標(biāo)方法之后,輸出日志時(shí)間:"+new Date());

        //返回目標(biāo)方法的執(zhí)行結(jié)果
        return  methodReturn;
    }
}

能否說一個(gè)前置通知+一個(gè)后置通知就等于一個(gè)環(huán)繞呢辛蚊?
答案是不可以的
環(huán)繞可以控制方法的返回值粤蝎,而前置、后置沒法袋马〕跖欤可以模擬在方法前后加返回值。

3.3.6 @AfterThrowing異常通知

語法:@AafterThrowing(value="切入點(diǎn)表達(dá)式",throwing="自定義變量")

package com.wh.ch09aspectaround.handle;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

/**
 * @Author :wanghui
 * @Date 2022/1/2 - 9:35
 */
@Aspect
public class MyAspect{
    /**
     * 異常通知方法的定義:
     * 1飞蛹、方法是public
     * 2谤狡、方法是沒有返回值的,是void的
     * 3卧檐、方法名稱自定義
     * 4墓懂、方法有參數(shù)是Exception
     */
    /**
     * @AfterThrowing:是異常通知’
     * 屬性:value:切入點(diǎn)表達(dá)式
     *      throwing:自定義變量,表示目標(biāo)方法拋出的異常霉囚,變量名必須和通知方法的形參名一樣
     * 位置:在方法的上面
     *
     * 特點(diǎn):
     * 1捕仔、在目標(biāo)方法拋出異常后執(zhí)行的,沒有異常不執(zhí)行
     * 2盈罐、能夠獲取目標(biāo)方法的異常信息
     * 3榜跌、不是異常處理程序,可以得到發(fā)生異常的通知盅粪,可以發(fā)送郵件钓葫、短信等通知開發(fā)人員
     *    可以看做是目標(biāo)方法的監(jiān)控程序
     * 異常通知的執(zhí)行
     * try{
     *     SomeServiceImpl.doSecond(..)
     * }catch(Exception e){
     *      myAfterThrowing(e)
     * }
     */
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing="ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("異常通知,在目標(biāo)方法拋出異常時(shí)候執(zhí)行的票顾,異常原因?yàn)椋?+ex.getMessage());
        /**
         * 異常發(fā)生時(shí):
         * 1础浮、記錄異常發(fā)生的時(shí)間、位置等信息
         * 2奠骄、發(fā)送郵件豆同、短信等通知開發(fā)人員
         */

    }
}

3.3.7@After 最終通知

語法:@After(value="切入點(diǎn)表達(dá)式")

package com.wh.ch11after.handle;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Date;

/**
 * @Author :wanghui
 * @Date 2022/1/2 - 9:35
 */
@Aspect
public class MyAspect{
    /**
     * 最終通知方法的定義:
     * 1、方法是public
     * 2含鳞、方法是沒有返回值的影锈,是void的
     * 3、方法名稱自定義
     * 4蝉绷、方法有參數(shù)是Exception
     */

    /**
     * @After:最終通知
     * 屬性:value:切入點(diǎn)表達(dá)式
     * 位置:在方法的上邊
     *
     * 特點(diǎn):
     * 1鸭廷、在目標(biāo)方法之后執(zhí)行的
     * 2、總是會(huì)被執(zhí)行
     * 3潜必、可用用來做程序最后的收尾工作靴姿,例如:清楚臨時(shí)數(shù)據(jù)、變量磁滚、清理內(nèi)存
     *
     * 最終通知
     * try{
     *     業(yè)務(wù)方法
     * }finally{
     *     myAfter
     * }
     */
    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void myAfter(){
        System.out.println("最終通知佛吓,總是會(huì)被執(zhí)行");
    }
}

3.3.8 @Pointcut

定義和管理切入點(diǎn)的注解
@PointCcut(value="切入點(diǎn)表達(dá)式")

package com.wh.handle;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Date;

/**
 * @Author :wanghui
 * @Date 2022/1/2 - 9:35
 */
@Aspect
public class MyAspect{

    @Before(value = "mypt()")
    public void myBefore(){
        System.out.println("前置方法=========");
    }

    @After(value = "mypt()")
    public void myAfter(){
        System.out.println("最終通知宵晚,總是會(huì)被執(zhí)行");
    }

    /**
     * @pointcut:管理和定義切入點(diǎn)的,不是通知注解
     * 屬性:value,切入點(diǎn)表達(dá)式
     * 位置维雇,在一個(gè)自定義方法的上面淤刃,這個(gè)方法看做是切入點(diǎn)表達(dá)式的別名
     * 其他的通知注解中,可以使用方法別名吱型,就表示使用這個(gè)切入點(diǎn)表達(dá)式了
     */

    @Pointcut("execution(* *..SomeServiceImpl.doThird(..))")
    public void mypt(){
        //無需代碼
    }

}

3.4 AOP 總結(jié)

AOP是一種動(dòng)態(tài)的思想逸贾,目的是實(shí)現(xiàn)業(yè)務(wù)功能和非業(yè)務(wù)功能的解耦合。業(yè)務(wù)功能是獨(dú)立的模塊津滞,其他功能也是獨(dú)立的模塊铝侵。例如事務(wù)功能,日志功能等等触徐。讓這些事務(wù)咪鲜、日志功能是可以被復(fù)用的。

當(dāng)目標(biāo)方法需要一些功能時(shí)撞鹉,可以在不修改疟丙、不能修改源代碼的情況下,使用AOP技術(shù)鸟雏,在程序執(zhí)行期間享郊,生成代理對(duì)象,通過代理執(zhí)行我們的業(yè)務(wù)方法孝鹊,同時(shí)增加功能炊琉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市又活,隨后出現(xiàn)的幾起案子温自,更是在濱河造成了極大的恐慌,老刑警劉巖皇钞,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異松捉,居然都是意外死亡夹界,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門隘世,熙熙樓的掌柜王于貴愁眉苦臉地迎上來可柿,“玉大人,你說我怎么就攤上這事丙者「闯猓” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵械媒,是天一觀的道長目锭。 經(jīng)常有香客問我评汰,道長,這世上最難降的妖魔是什么痢虹? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任被去,我火速辦了婚禮,結(jié)果婚禮上奖唯,老公的妹妹穿的比我還像新娘惨缆。我一直安慰自己,他們只是感情好丰捷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布坯墨。 她就那樣靜靜地躺著,像睡著了一般病往。 火紅的嫁衣襯著肌膚如雪捣染。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天荣恐,我揣著相機(jī)與錄音液斜,去河邊找鬼。 笑死叠穆,一個(gè)胖子當(dāng)著我的面吹牛少漆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硼被,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼示损,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了嚷硫?” 一聲冷哼從身側(cè)響起检访,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仔掸,沒想到半個(gè)月后脆贵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡起暮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年卖氨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片负懦。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筒捺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纸厉,到底是詐尸還是另有隱情系吭,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布颗品,位于F島的核電站肯尺,受9級(jí)特大地震影響沃缘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蟆盹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一孩灯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逾滥,春花似錦峰档、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舔哪,卻和暖如春欢顷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捉蚤。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工抬驴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缆巧。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓布持,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陕悬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子题暖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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