SpringBoot 使用AOP

SpringBoot AOP

AOP(面向切面編程)是SpringBoot的兩大核心功能之一,功能非常強大,為解耦提供了非常優(yōu)秀的解決方案。

AOP術(shù)語

  • 執(zhí)行點(Excutepoint):類初始化,方法調(diào)用
  • 連接點(JoinPoint):執(zhí)行點 和 方位的組合,可以確定JoinPoint葱椭。比如類初始化前,初始化后口四,方法調(diào)用前孵运,方法調(diào)用后。
  • 切點(PointCut):在眾多的執(zhí)行點中蔓彩,定位合適的執(zhí)行點治笨。ExcutePoint相當于數(shù)據(jù)庫中的記錄驳概,Pointcut相當于查詢條件。
  • 增強(Advice):織入到目標類連接點上的一段代碼旷赖,除了代碼之后顺又,還有執(zhí)行點的方位信息。
  • 目標對象(Target):增強邏輯的織入目標類等孵。
  • 引介(Introduction):一種特殊的增強稚照,為類增加一些額外的屬性和方法,動態(tài)為業(yè)務(wù)類增加其他接口的實現(xiàn)邏輯俯萌,讓業(yè)務(wù)類成為這個接口的實現(xiàn)類果录。
  • 代理(proxy):一個類被AOP織入后,產(chǎn)生了一個結(jié)果類咐熙,它便是融合了原類和增強邏輯的代理類弱恒。
  • 切面(Aspect):切面由切點和增強組成既包括橫切邏輯定義,也包括連接點定義糖声。

AOP重點:

  • 如何通過切點和增強定位到連接點
  • 如何在增強中編寫切面的代碼

實現(xiàn)方式

  • 添加MAVEN依賴

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    

正則匹配

  • 創(chuàng)建切面類

    /**
     * 日志切面
     */
    
    @Aspect
    @Component
    public class LogAspect {
        @Pointcut("execution(public * com.xncoding.aop.controller.*.*(..))")
        public void webLog(){}
    
        @Before("webLog()")
        public void deBefore(JoinPoint joinPoint) throws Throwable {
            // 接收到請求斤彼,記錄請求內(nèi)容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 記錄下請求內(nèi)容
            System.out.println("URL : " + request.getRequestURL().toString());
            System.out.println("HTTP_METHOD : " + request.getMethod());
            System.out.println("IP : " + request.getRemoteAddr());
            System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    
        }
    
        @AfeterReturning(returning = "ret", pointcut = "webLog()")
        public void doAfterReturning(Object ret) throws Throwable {
            // 處理完請求分瘦,返回內(nèi)容
            System.out.println("方法的返回值 : " + ret);
        }
    
        //后置異常通知
        @AfterThrowing("webLog()")
        public void throwss(JoinPoint jp){
            System.out.println("方法異常時執(zhí)行.....");
        }
    
        //后置最終通知,final增強蘸泻,不管是拋出異常或者正常退出都會執(zhí)行
        @After("webLog()")
        public void after(JoinPoint jp){
            System.out.println("方法最后執(zhí)行.....");
        }
    
        //環(huán)繞通知,環(huán)繞增強嘲玫,相當于MethodInterceptor
        @Around("webLog()")
        public Object arround(ProceedingJoinPoint pjp) {
            System.out.println("方法環(huán)繞start.....");
            try {
                Object o =  pjp.proceed();
                System.out.println("方法環(huán)繞proceed悦施,結(jié)果是 :" + o);
                return o;
            } catch (Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    

    結(jié)果:

    方法環(huán)繞start.....
    URL : http://localhost:8092/first
    HTTP_METHOD : GET
    IP : 0:0:0:0:0:0:0:1
    CLASS_METHOD : com.xncoding.aop.controller.UserController.first
    ARGS : []
    方法環(huán)繞proceed,結(jié)果是 :first controller
    方法最后執(zhí)行.....
    方法的返回值 : first controller
    

    切面注解說明

    • @Aspect 作用是把當前類標識為一個切面供容器讀取
    • @Pointcut 定義切點去团,切點方法不用任何代碼抡诞,返回值是void,重要的是條件表達式
    • @Before 標識一個前置增強方法土陪,相當于BeforeAdvice的功能
    • @AfterReturning 后置增強昼汗,相當于AfterReturningAdvice,方法退出時執(zhí)行
    • @AfterThrowing 異常拋出增強鬼雀,相當于ThrowsAdvice
    • @After final增強顷窒,不管是拋出異常或者正常退出都會執(zhí)行
    • @Around 環(huán)繞增強源哩,相當于MethodInterceptor

    方法參數(shù)說明

    除了@Around外鞋吉,每個方法里都可以加或者不加參數(shù)JoinPoint。

    JoinPoint里包含了類名励烦、被切面的方法名谓着,參數(shù)等屬性,可供讀取使用坛掠。

    @Around參數(shù)必須為ProceedingJoinPoint赊锚,pjp.proceed相應(yīng)于執(zhí)行被切面的方法治筒。

    @AfterReturning方法里,可以加returning = “xxx”舷蒲,xxx即為在controller里方法的返回值矢炼,本例中的返回值是”first controller”。

    @AfterThrowing方法里阿纤,可以加throwing = “XXX”句灌,讀取異常信息,如本例中可以改為:

    //后置異常通知
    @AfterThrowing(throwing = "ex", pointcut = "webLog()")
    public void throwss(JoinPoint jp, Exception ex){
        System.out.println("方法異常時執(zhí)行.....");
    }
    

    一般常用的有before和afterReturn組合欠拾,或者單獨使用Around胰锌,即可獲取方法開始前和結(jié)束后的切面。

    關(guān)于切點PointCut

    execution函數(shù)用于匹配方法執(zhí)行的連接點藐窄,語法為:

    execution(方法修飾符(可選) 返回類型 方法名 參數(shù) 異常模式(可選))

    參數(shù)部分允許使用通配符:

    表達式可由多個切點函數(shù)通過邏輯運算組成资昧,與(&&)、 或(||)荆忍、 非(!)

使用注解實現(xiàn)AOP

自定義注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Rentention(RetentionPolicy.RUNTIME)
public  UserAccess {
    String desc() default "無信息";
}

創(chuàng)建切面類

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Component
@Aspect
public class UserAccessAspect {

    @Pointcut(value = "@annotation(com.xncoding.aop.aspect.UserAccess)")
    public void access() {

    }

    @Before("access()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println("second before");
    }

    @Around("@annotation(userAccess)")
    public Object around(ProceedingJoinPoint pjp, UserAccess userAccess) {
        //獲取注解里的值
        System.out.println("second around:" + userAccess.desc());
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
}

主要看一下@Around注解這里格带,如果需要獲取在controller注解中賦給UserAccess的desc里的值,就需要這種寫法刹枉,這樣UserAccess參數(shù)就有值了叽唱。

spring aop就是一個同心圓,要執(zhí)行的方法為圓心微宝,最外層的order最小棺亭。從最外層按照AOP1、AOP2的順序依次執(zhí)行doAround方法蟋软,doBefore方法镶摘。然后執(zhí)行method方法,最后按照AOP2岳守、AOP1的順序依次執(zhí)行doAfter凄敢、doAfterReturn方法。也就是說對多個AOP來說湿痢,先before的涝缝,一定后after。

對于上面的例子就是蒙袍,先外層的就是對所有controller的切面俊卤,內(nèi)層就是自定義注解的。 那不同的切面害幅,順序怎么決定呢消恍,尤其是同格式的切面處理,譬如兩個execution的情況以现,那spring就是隨機決定哪個在外哪個在內(nèi)了狠怨。

所以大部分情況下约啊,我們需要指定順序,最簡單的方式就是在Aspect切面類上加上@Order(1)注解即可佣赖,order越小最先執(zhí)行恰矩,也就是位于最外層。像一些全局處理的就可以把order設(shè)小一點憎蛤,具體到某個細節(jié)的就設(shè)大一點

參考:https://www.xncoding.com/2017/07/24/spring/sb-aop.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末外傅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子俩檬,更是在濱河造成了極大的恐慌萎胰,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棚辽,死亡現(xiàn)場離奇詭異技竟,居然都是意外死亡,警方通過查閱死者的電腦和手機屈藐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門榔组,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人联逻,你說我怎么就攤上這事搓扯。” “怎么了遣妥?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵擅编,是天一觀的道長。 經(jīng)常有香客問我箫踩,道長,這世上最難降的妖魔是什么谭贪? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任境钟,我火速辦了婚禮,結(jié)果婚禮上俭识,老公的妹妹穿的比我還像新娘慨削。我一直安慰自己,他們只是感情好套媚,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布缚态。 她就那樣靜靜地躺著,像睡著了一般堤瘤。 火紅的嫁衣襯著肌膚如雪玫芦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天本辐,我揣著相機與錄音桥帆,去河邊找鬼医增。 笑死,一個胖子當著我的面吹牛老虫,可吹牛的內(nèi)容都是我干的叶骨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼祈匙,長吁一口氣:“原來是場噩夢啊……” “哼忽刽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起夺欲,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缔恳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洁闰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歉甚,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年扑眉,在試婚紗的時候發(fā)現(xiàn)自己被綠了纸泄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡腰素,死狀恐怖聘裁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弓千,我是刑警寧澤衡便,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站洋访,受9級特大地震影響镣陕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姻政,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一呆抑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汁展,春花似錦鹊碍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至器紧,卻和暖如春耀销,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背品洛。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工树姨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留摩桶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓帽揪,卻偏偏與公主長得像硝清,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子转晰,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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