Spring AOP有多少個通知以及它們的執(zhí)行順序介紹
Spring AOP有多少個通知以及它們的執(zhí)行順序
Spring AOP有多少個通知
- ①前置通知(Before):在連接點執(zhí)行前執(zhí)行該通知
- ②正常返回通知(AfterReturning):在連接點正常執(zhí)行完后執(zhí)行該通知,若目標方法執(zhí)行異常則不會執(zhí)行該通知
- ③異常通知(AfterThrowing):在連接點執(zhí)行拋出異常時執(zhí)行該通知
- ④后置通知(after/finally):在連接點執(zhí)行完成后(不管成功、失敗、異常)都會執(zhí)行該通知
- ⑤環(huán)繞通知(Around):圍繞在連接點前后
Spring AOP通知的執(zhí)行順序
- ①環(huán)繞通知:@Around
- ②前置通知:@Before
- ③執(zhí)行連接點方法
- ④環(huán)繞通知:@Around
- ⑤后置通知:@After
- ⑥正常返回通知:@AfterReturning,如果發(fā)生異常那么就是異常通知@AfterThrowing
SpringAOP簡單案例
本文是一個老師在學校給學生上課的簡單案例,介紹了AOP的五個通知的使用,以及通知的執(zhí)行順序。通過自定義注解來充當切入點,獲取注解的類型分別對不同的老師做對應的業(yè)務處理。
代碼中的消息響應體(Result)大家可以自定義類型。
AOP的五大通知
- 前置通知:Before
- 環(huán)繞通知:Around
- 后置通知:After
- 后置返回通知:AfterReturning
- 后置異常通知:AfterThrowing
執(zhí)行順序如下圖所示:
AOP的使用方式
1.創(chuàng)建一個課題實體對象
package com.cloud.industryapi.test; import lombok.Data; /** * 課題實體 * @date 2022/3/25 16:26 */ @Data public class ArticleEntity { /** * PK */ private Integer id; /** * 課題 */ private String title; /** * 內(nèi)容 */ private String content; }
2.定義一個切入點,這里以自定義注解的方式實現(xiàn)
package com.cloud.industryapi.test; import java.lang.annotation.*; /** * 切點標識 * @author * @date 2022/3/25 13:09 */ @Target({ElementType.METHOD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface PointcutId { String type() default ""; }
3.聲明要織入的切面
package com.cloud.industryapi.test; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * 切面 */ @Slf4j @Aspect @Component public class ArticleAspect { /** * 定義切入點 */ @Pointcut("@annotation(com.cloud.industryapi.test.PointcutId)") public void pointcut(){ } /** * 環(huán)繞通知 * @param joinPoint * @param id * @return * @throws Throwable */ @Around("pointcut() && @annotation(id)") public Object around(ProceedingJoinPoint joinPoint, PointcutId id) throws Throwable { log.info("-------------around start-------------"); Object[] objects = joinPoint.getArgs(); switch (id.type()){ case "language": ArticleEntity language = (ArticleEntity) objects[0]; log.info("-------------語文老師課前備課,課題:{}-------------",language.getTitle()); break; case "mathematics": log.info("-------------數(shù)學老師課前備課-------------"); break; default: throw new RuntimeException("類型非法"); } //joinPoint.proceed() Object s = joinPoint.proceed(); log.info("-------------叮鈴鈴鈴鈴...放學了-------------"); log.info("-------------around end-------------"); return s; } /** * 前置通知 * @param joinPoint * @param id */ @Before("pointcut() && @annotation(id)") public void before(JoinPoint joinPoint,PointcutId id){ log.info("-------------before start-------------"); log.info("-------------學生進入教室,準備上課-------------"); log.info("-------------before end-------------"); } /** * 后置通知 * @param joinPoint * @param id */ @After("pointcut() && @annotation(id)") public void after(JoinPoint joinPoint,PointcutId id){ log.info("-------------after start-------------"); log.info("-------------學校廣播:老師們,同學們,中午好,今天學校食堂免費為你們準備了燒雞,人手一雞-------------"); log.info("-------------after end-------------"); } /** * 后置返回通知 * @param joinPoint * @param id */ @AfterReturning("pointcut() && @annotation(id)") public void afterReturn(JoinPoint joinPoint, PointcutId id){ log.info("-------------AfterReturning-------------"); log.info("-------------老師們同學們拿著燒雞回家了-------------"); log.info("-------------學校關閉了大門-------------"); log.info("-------------AfterReturning-------------"); } /** * 后置異常通知 * @param joinPoint * @param id */ @AfterThrowing("pointcut() && @annotation(id)") public void afterThrow(JoinPoint joinPoint,PointcutId id){ log.info("-------------AfterThrowing-------------"); log.info("-------------完蛋,小明同學迷路了。。。-------------"); log.info("-------------AfterThrowing-------------"); } }
注意:ProceedingJoinPoint的proceed()方法相當于前置通知和后置通知的分水嶺。
說明:ProceedingJoinPoint的proceed()方法在執(zhí)行前用來做一些
- 例如:讀取日志 ,然后執(zhí)行目標方法。ProceedingJoinPoint的proceed()方法執(zhí)行后 ,用來做一些
- 例如:寫入日志\color{#0000FF}{說明:ProceedingJoinPoint的proceed()方法在執(zhí)行前用來做一些
- 例如:讀取日志,然后執(zhí)行目標方法。ProceedingJoinPoint的proceed()方法執(zhí)行后,用來做一些
- 例如:寫入日志}說明:ProceedingJoinPoint的proceed()方法在執(zhí)行前用來做一些
- 例如:讀取日志,然后執(zhí)行目標方法。ProceedingJoinPoint的proceed()方法執(zhí)行后,用來做一些
- 例如:寫入日志
4.編寫控制器
package com.cloud.industryapi.test; import com.cloud.common.kit.Result; import com.cloud.common.page.FrontPagination; import com.cloud.industry.dto.ExclusiveJumpConfigDto; import com.cloud.industry.facede.ExclusiveJumpConfigFacede; import com.cloud.industry.qo.ExclusiveJumpConfigQo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * 文章 - 控制器 */ @Slf4j @RestController @RequestMapping("/aop/test") public class ArticleController { /** * 后置異常通知 */ @PointcutId @RequestMapping("/afterThrow") public void afterThrow(){ log.info("-------------------進入了目標方法-------------------"); System.out.println(2/0); } /** * 語文課 * * @param ae * @return */ @PointcutId(type = "language") @PostMapping("/language") public Result language(@RequestBody ArticleEntity ae) { log.info("-------------目標方法開始執(zhí)行-------------"); log.info("-------------語文老師進入教室,開始講課《"+ae.getTitle()+"》-------------"); System.out.printf("%s","深藍的天空中掛著一輪金黃的圓月,下面是海邊的沙地,都種著一望無際的碧綠的西瓜。\n" + "其間有一個十一二歲的少年,項帶銀圈,手捏一柄鋼叉,向一匹猹盡力的刺去。\n" + "那猹卻將身一扭,反從他的胯下逃走了。\n"); log.info("-------------目標方法結(jié)束執(zhí)行-------------"); return Result.success("執(zhí)行結(jié)束"); } /** * 數(shù)學課 * * @param ae * @return */ @PointcutId(type = "mathematics") @PostMapping("/mathematics") public Result mathematics(@RequestBody ArticleEntity ae) { log.info("-------------目標方法開始執(zhí)行-------------"); log.info("-------------數(shù)學老師進入教室-------------"); log.info("-------------“同學們,今天這節(jié)數(shù)學課,由我代上”-------------"); log.info("-------------“起立”-------------"); log.info("-------------”體~育~老~師~好~~“-------------"); log.info("-------------目標方法結(jié)束執(zhí)行-------------"); return Result.success("執(zhí)行結(jié)束"); } }
5.請求控制器
最后是請求的響應
完成
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Springboot整合Flowable6.x導出bpmn20的步驟詳解
這篇文章主要介紹了Springboot整合Flowable6.x導出bpmn20,Flowable流程引擎可用于部署B(yǎng)PMN 2.0流程定義,可以十分靈活地加入你的應用/服務/構(gòu)架,本文給出兩種從flowable導出流程定義bpmn20.xml的方式,需要的朋友可以參考下2023-04-04Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 上
在并發(fā)編程中存在線程安全問題,主要原因有:1.存在共享數(shù)據(jù) 2.多線程共同操作共享數(shù)據(jù)。關鍵字synchronized可以保證在同一時刻,只有一個線程可以執(zhí)行某個方法或某個代碼塊,同時synchronized可以保證一個線程的變化可見(可見性),即可以代替volatile2021-09-09