SpringBoot中切面的解釋和實(shí)例

注:本文轉(zhuǎn)載自 航歌-開發(fā)者知識(shí)平臺(tái)

作者:hangge

[原帖地址]{https://www.hangge.com/blog/cache/detail_2527.html}

一、基本介紹

1亩进,什么是 AOP

(1)AOP 為 Aspect Oriented Programming 的縮寫灵汪,意為:面向切面編程牺汤,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)咳短。

(2)利用 AOP 可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低评也,提高程序的可重用性攒磨,同時(shí)提高了開發(fā)的效率。

一個(gè) AOP 的使用場(chǎng)景:
假設(shè)一個(gè)已經(jīng)上線的系統(tǒng)運(yùn)行出現(xiàn)問(wèn)題蜗字,有時(shí)運(yùn)行得很慢打肝。為了檢測(cè)出是哪個(gè)環(huán)節(jié)出現(xiàn)了問(wèn)題,就需要監(jiān)控每一個(gè)方法的執(zhí)行時(shí)間挪捕,再根據(jù)執(zhí)行時(shí)間進(jìn)行分析判斷粗梭。
由于整個(gè)系統(tǒng)里的方法數(shù)量十分龐大,如果一個(gè)個(gè)方法去修改工作量將會(huì)十分巨大级零,而且這些監(jiān)控方法在分析完畢后還需要移除掉断医,所以這種方式并不合適。
如果能夠在系統(tǒng)運(yùn)行過(guò)程中動(dòng)態(tài)添加代碼奏纪,就能很好地解決這個(gè)需求鉴嗤。這種在系統(tǒng)運(yùn)行時(shí)動(dòng)態(tài)添加代碼的方式稱為面向切面編程(AOP)

2,AOP 相關(guān)概念介紹

  • Joinpoint(連接點(diǎn)):類里面可以被增強(qiáng)的方法即為連接點(diǎn)序调。例如醉锅,想要修改哪個(gè)方法的功能,那么該方法就是一個(gè)鏈接點(diǎn)发绢。
  • Target(目標(biāo)對(duì)象):要增強(qiáng)的類成為 Target硬耍。
  • Pointcut(切入點(diǎn)):對(duì) Jointpoint 進(jìn)行攔截的定義即為切入點(diǎn)。例如边酒,攔截所有以 insert 開始的方法经柴,這個(gè)定義即為切入點(diǎn)。
  • Advice(通知):攔截到 Jointpoint 之后要做的事情就是通知甚纲。通知分為前置通知口锭、后置通知、異常通知介杆、最終通知和環(huán)繞通知鹃操。例如,前面說(shuō)到的打印日志監(jiān)控就是通知春哨。
  • Aspect(切面):即 Pointcut 和 Advice 的結(jié)合荆隘。

3,安裝配置

Spring Boot 在 Spring 的基礎(chǔ)上對(duì) AOP 的配置提供了自動(dòng)化配置解決方案赴背,我們只需要修改 pom.xml 文件椰拒,添加 spring-boot-starter-aop 依賴即可晶渠。

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

二、使用樣例

1燃观,創(chuàng)建 Service

首先創(chuàng)建一個(gè) UserService(假設(shè)在 com.example.demo.service)褒脯,內(nèi)容如下

@Service
public class UserService {
   public String getUserById(Integer id) {
       System.out.println("getUserById(" + id + ")...");
       // 等待2秒
       try {
           Thread.sleep(2000);
       }
       catch(InterruptedException e) {
           e.printStackTrace();
       }
       return "hangge";
   }
}

2,創(chuàng)建切面

接著定義一個(gè)切面類缆毁,代碼如下

注解說(shuō)明:
(1)@Aspect 注解:表明這是一個(gè)切面類番川。
(2)@Pointcut 注解:表明這是一個(gè)切入點(diǎn)。
execution 中的第一個(gè) * 表示方法返回任意值
第二個(gè) * 表示 service 包下的任意類
第三個(gè) * 表示類中的任意方法脊框,括號(hào)中的兩個(gè)點(diǎn)表示方法參數(shù)任意颁督,即這里描述的切入點(diǎn)為 service 包下所有類中的所有方法。
(3)@Before 注解:表示這是一個(gè)前置通知浇雹,該方法在目標(biāo)方法之前執(zhí)行沉御。
通過(guò) JoinPoint 參數(shù)可以獲取目標(biāo)方法的方法名、修飾符等信息昭灵。
(4)@After 注解:表示這是一個(gè)后置通知吠裆,該方法在目標(biāo)執(zhí)行之后執(zhí)行。
(5)@AfterReturning 注解:表示這是一個(gè)返回通知烂完,在該方法中可以獲取目標(biāo)方法的返回值硫痰。
returning 參數(shù)是指返回值的變量名,對(duì)應(yīng)方法的參數(shù)窜护。
注意:本樣例在方法參數(shù)中定義 result 的類型為 Object效斑,表示目標(biāo)方法的返回值可以是任意類型。若 result 參數(shù)的類型為 Long柱徙,則該方法只能處理目標(biāo)方法返回值為 Long 的情況缓屠。
(6)@AfterThrowing 注解:表示這是一個(gè)異常通知,即當(dāng)目標(biāo)方法發(fā)生異常护侮,該方法會(huì)被調(diào)用敌完。
樣例中設(shè)置的異常類型為 Exception 表示所有的異常都會(huì)進(jìn)入該方法中執(zhí)行。
若異常類型為 ArithmeticException 則表示只有目標(biāo)方法拋出的 ArithmeticException 異常才會(huì)進(jìn)入該方法的處理羊初。
(7) @Around 注解:表示這是一個(gè)環(huán)繞通知滨溉。環(huán)繞通知是所有通知里功能最為強(qiáng)大的通知,可以實(shí)現(xiàn)前置通知长赞、后置通知晦攒、異常通知以及返回通知的功能。
目標(biāo)方法進(jìn)入環(huán)繞通知后得哆,通過(guò)調(diào)用 ProceedingJointPoint 對(duì)象的 proceed 方法使目標(biāo)方法繼續(xù)執(zhí)行脯颜,開發(fā)者可以在次修改目標(biāo)方法的執(zhí)行參數(shù)、返回值值贩据,并且可以在此目標(biāo)方法的異常

@Aspect
@Component
public class LogAspect {
    // 定義一個(gè)切入點(diǎn)
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void pc1(){
 
    }
 
    // 前置通知
    @Before(value = "pc1()")
    public void before(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法開始執(zhí)行...");
    }
 
    // 后置通知
    @After(value = "pc1()")
    public void after(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法執(zhí)行結(jié)束...");
    }
 
    // 返回通知
    @AfterReturning(value = "pc1()", returning = "result")
    public void afterReturning(JoinPoint jp, Object result) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法返回值為:" + result);
    }
 
    // 異常通知
    @AfterThrowing(value = "pc1()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法拋異常了栋操,異常是:" + e.getMessage());
    }
 
    // 環(huán)繞通知
    @Around("pc1()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String name = pjp.getSignature().getName();
        // 統(tǒng)計(jì)方法執(zhí)行時(shí)間
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long end = System.currentTimeMillis();
        System.out.println(name + "方法執(zhí)行時(shí)間為:" + (end - start) + " ms");
        return result;
    }
}

3闸餐,創(chuàng)建 Controller

配置完成后,接下來(lái)在 Controller 中創(chuàng)建接口調(diào)用 UserService 中的方法矾芙。

@RestController
public class HelloController {
 
    @Autowired
    UserService userService;
 
    @GetMapping("/test")
    public String test(Integer id) {
        return userService.getUserById(id);
    }
}

4舍沙,運(yùn)行樣例

(1)使用瀏覽器訪問(wèn)如下地址:

  • http://localhost:8080/test?id=11
    (2)查看控制臺(tái)信息,可以發(fā)現(xiàn) LogAspect 中的代碼動(dòng)態(tài)地嵌入目標(biāo)方法中執(zhí)行了
    image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剔宪,一起剝皮案震驚了整個(gè)濱河市场勤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歼跟,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件格遭,死亡現(xiàn)場(chǎng)離奇詭異哈街,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)拒迅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門骚秦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人璧微,你說(shuō)我怎么就攤上這事作箍。” “怎么了前硫?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵胞得,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我屹电,道長(zhǎng)阶剑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任危号,我火速辦了婚禮牧愁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘外莲。我一直安慰自己猪半,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布偷线。 她就那樣靜靜地躺著磨确,像睡著了一般。 火紅的嫁衣襯著肌膚如雪声邦。 梳的紋絲不亂的頭發(fā)上俐填,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音翔忽,去河邊找鬼英融。 笑死盏檐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驶悟。 我是一名探鬼主播胡野,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痕鳍!你這毒婦竟也來(lái)了硫豆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤笼呆,失蹤者是張志新(化名)和其女友劉穎熊响,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诗赌,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汗茄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铭若。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洪碳。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖叼屠,靈堂內(nèi)的尸體忽然破棺而出瞳腌,到底是詐尸還是另有隱情,我是刑警寧澤镜雨,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布嫂侍,位于F島的核電站,受9級(jí)特大地震影響荚坞,放射性物質(zhì)發(fā)生泄漏吵冒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一西剥、第九天 我趴在偏房一處隱蔽的房頂上張望痹栖。 院中可真熱鬧,春花似錦瞭空、人聲如沸揪阿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)南捂。三九已至,卻和暖如春旧找,著一層夾襖步出監(jiān)牢的瞬間溺健,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工钮蛛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鞭缭,地道東北人剖膳。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像岭辣,于是被迫代替她去往敵國(guó)和親吱晒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

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