Springboot 中使用 Aop代碼實(shí)戰(zhàn)教程
1. aop作用
AOP意為面向切面編程,可以通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)。AOP的編程思想是把對(duì)類對(duì)象的橫切問題點(diǎn),從業(yè)務(wù)邏輯中分離出來,從而達(dá)到解耦的目的,增加代碼的復(fù)用性,提高開發(fā)效率。
2. 相關(guān)注解
@Component :將當(dāng)前類注入到Spring容器內(nèi)。
@Aspect :表明當(dāng)前類是一個(gè)切面類。
@Before :前置通知,在被切入方法執(zhí)行之前執(zhí)行。
@After :后置通知,在被切入方法執(zhí)行之后執(zhí)行。
@AfterRuturning :返回通知,在被切入方法返回結(jié)果之后執(zhí)行。使用此注解的方法,可以使用returning = "XXX"返回被切入方法的返回值,XXX即為被切入方法的返回值,本例中是controller類中方法的返回值。
//返回通知 @AfterReturning(returning = "ret", pointcut = "log()") public void doAfterReturning(Object ret) throws Throwable { // 處理完請(qǐng)求,返回內(nèi)容 System.out.println("返回通知:方法的返回值 : " + ret); }
@AfterThrowing :異常通知,在被切入方法拋出異常之后執(zhí)行。在此注解中可以使用throwing = "XXX"獲取異常信息,獲取的異常信息可以在方法中打印出來,舉例如下。
/
/異常通知 @AfterThrowing(throwing = "ex", pointcut = "log()") public void throwss(JoinPoint jp, Exception ex) { System.out.println("異常通知:方法異常時(shí)執(zhí)行....."); System.out.println("產(chǎn)生異常的方法:" + jp); System.out.println("異常種類:" + ex); }
@Around :環(huán)繞通知,圍繞著被切入方法執(zhí)行。參數(shù)必須為ProceedingJoinPoint,pjp.proceed相應(yīng)于執(zhí)行被切面的方法。環(huán)繞通知一般單獨(dú)使用,環(huán)繞通知可以替代上面的四種通知,后面單獨(dú)介紹。@Pointcut :切入點(diǎn),PointCut(切入點(diǎn))表達(dá)式有很多種,其中execution用于使用切面的連接點(diǎn)。
注意:返回通知和異常通知只能有一個(gè)會(huì)被執(zhí)行,因?yàn)榘l(fā)生異常執(zhí)行異常通知,然后就不會(huì)繼續(xù)向下執(zhí)行,自然后置通知也就不會(huì)被執(zhí)行,反之亦然。后置通知一定會(huì)執(zhí)行。
3. Aop 相關(guān)概念
Joinpoint(連接點(diǎn)):所謂連接點(diǎn)是指那些被攔截到的點(diǎn),在 spring 中,這些點(diǎn)指的是方法,因?yàn)?spring 只支持方法類型的連接點(diǎn),通俗的說就是被增強(qiáng)類中的所有方法。注意: 除了環(huán)繞通知外,其他的四個(gè)通知注解中,加或者不加參數(shù)JoinPoint都可以,如果有用到JoinPoint的地方就加,用不到就可以不加。JoinPoint里包含了類名、被切面的方法名,參數(shù)等屬性。PointCut(切入點(diǎn)):所謂切入點(diǎn)是指我們要對(duì)哪些 Joinpoint 進(jìn)行攔截的定義,通俗的說就是被增強(qiáng)類中的被增強(qiáng)的方法,因?yàn)楸辉鰪?qiáng)類中并不是所有的方法都被增強(qiáng)了。Advice(通知/增強(qiáng)):所謂通知是指攔截到 Joinpoint (被增強(qiáng)的方法)之后所要做的事情就是通知,通俗的說就是對(duì)被增強(qiáng)的方法進(jìn)行增強(qiáng)的代碼Aspect(切面):是切入點(diǎn)和通知(引介)的結(jié)合,通俗的說就是建立切入點(diǎn)和通知方法在創(chuàng)建時(shí)的對(duì)應(yīng)關(guān)系。
4. 切入點(diǎn)表達(dá)式詳解:@PointCut(表達(dá)式)
PointCut:切入點(diǎn),指哪些方法需要被執(zhí)行AOP,PointCut表達(dá)式可以有一下幾種方式:
1.execution表達(dá)式
表達(dá)式語法:訪問修飾符 返回值 包名.包名.包名…類名.方法名(參數(shù)列表)標(biāo)準(zhǔn)的表達(dá)式寫法范例:
public void com.aismall.testaop.controller.HelloController.helloAop()
訪問修飾符可以省略
void com.aismall.testaop.controller.HelloController.helloAop()
返回值可以使用通配符*,表示任意返回值
* com.aismall.testaop.controller.HelloController.helloAop()
包名可以使用通配符,表示任意包,但是有幾級(jí)包,就需要寫幾個(gè)*.
* *.*.*.*.HelloController.helloAop()
包名可以使用…表示當(dāng)前包及其子包
* *...HelloController.helloAop()
類名和方法名都可以使用*來實(shí)現(xiàn)通配
* *..*.*()
表達(dá)式中的參數(shù)列表,可以直接寫數(shù)據(jù)類型:
基本類型直接寫名稱 :例如,int
引用類型寫包名.類名的方式 :例如,java.lang.String
可以使用通配符表示任意類型,但是必須有參數(shù)
可以使用…表示有無參數(shù)均可,有參數(shù)可以是任意類型
全通配寫法: ….*(…)
2.within表達(dá)式是用來指定類型的,指定類型中的所有方法將被攔截表達(dá)式:包名.包名.包名…類名標(biāo)準(zhǔn)的表達(dá)式寫法范例:
com.aismall.testaop.controller.HelloController
舉例:匹配HelloController類對(duì)應(yīng)對(duì)象的所有方法,并且只能是HelloController類生成的對(duì)象,不能是它的子類生成的對(duì)象。
within(com.aismall.testaop.controller.HelloController)
也可以使用通配符*:匹配com.aismall包及其子包下面的所有類的所有方法。
within(com.aismall…*)
3.this(type)
SpringAOP是基于代理的,this就代表代理對(duì)象,語法是this(type),當(dāng)生成的代理對(duì)象可以轉(zhuǎn)化為type指定的類型時(shí)表示匹配。this(com.aismall.testaop.controller.HelloController)
匹配生成的代理對(duì)象是HelloController類型的所有方法的外部調(diào)用
4.target
SpringAOP是基于代理的,target表示被代理的目標(biāo)對(duì)象,當(dāng)被代理的目標(biāo)對(duì)象可以轉(zhuǎn)換為指定的類型時(shí)則表示匹配。target(com.aismall.testaop.controller.HelloController)
匹配所有被代理的目標(biāo)對(duì)象能夠轉(zhuǎn)化成HelloController類型的所有方法的外部調(diào)用。
5.args:
args用來匹配方法參數(shù)
args() 匹配不帶參數(shù)的方法
args(java.lang.String) 匹配方法參數(shù)是String類型的
args(…) 帶任意參數(shù)的方法
args(java.lang.String,…) 匹配第一個(gè)參數(shù)是String類型的,其他參數(shù)任意。最后一個(gè)參數(shù)是String的同理。
6.@ annotation:帶有相應(yīng)注解的方法,比如對(duì)標(biāo)有@Transactional注解的方法進(jìn)行增強(qiáng)
@annotation(org.springframework.transaction.annotation.Transactional)
@within和@target針對(duì)類的注解@annotation針對(duì)方法的注解@args:參數(shù)帶有相應(yīng)標(biāo)注的任意方法,比如@Transactional
@args(org.springframework.transaction.annotation.Transactional)
5. 代碼實(shí)戰(zhàn) 使用execution(表達(dá)式)
創(chuàng)建一個(gè)aspect切面類
@Aspect @Component public class MyAop { //切入點(diǎn):待增強(qiáng)的方法 @Pointcut("execution(public * com.xxx.controller.*.*(..))") //切入點(diǎn)簽名 public void log(){ System.out.println("pointCut簽名。。。"); } //前置通知 @Before("log()") public void deBefore(JoinPoint jp) throws Throwable { // 接收到請(qǐng)求,記錄請(qǐng)求內(nèi)容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 記錄下請(qǐng)求內(nèi)容 System.out.println("URL : " + request.getRequestURL().toString()); System.out.println("HTTP_METHOD : " + request.getMethod()); System.out.println("CLASS_METHOD : " + jp); System.out.println("ARGS : " + Arrays.toString(jp.getArgs())); } //返回通知 @AfterReturning(returning = "ret", pointcut = "log()") public void doAfterReturning(Object ret) throws Throwable { // 處理完請(qǐng)求,返回內(nèi)容 System.out.println("返回通知:方法的返回值 : " + ret);`在這里插入代碼片` } //異常通知 @AfterThrowing(throwing = "ex", pointcut = "log()") public void throwss(JoinPoint jp,Exception ex){ System.out.println("異常通知:方法異常時(shí)執(zhí)行....."); System.out.println("產(chǎn)生異常的方法:"+jp); System.out.println("異常種類:"+ex); } //后置通知 @After("log()") public void after(JoinPoint jp){ System.out.println("后置通知:最后且一定執(zhí)行....."); } }
創(chuàng)建controlle
@RestController public class HelloController { @RequestMapping("/helloAop") public Object hello(){ return "hello aop"; } @RequestMapping("/helloError") public Object helloError(){ return 1/0; } }
測試
6. 代碼實(shí)戰(zhàn) 使用@annotation
自定義注解
//表示次注解可以標(biāo)注在類和方法上 @Target({ElementType.METHOD, ElementType.TYPE}) //運(yùn)行時(shí)生效 @Retention(RetentionPolicy.RUNTIME) public @interface MyLogAnnotation { //定義一個(gè)變量,可以接受參數(shù) String desc() default " "; }
切面類
@Aspect @Component public class MyAopAnnotation { //切入點(diǎn):增強(qiáng)標(biāo)有MyLogAnnotation注解的方法 @Pointcut(value="@annotation(com.aismall.testaop.MyAnnotation.MyLogAnnotation)") //切入點(diǎn)簽名 public void logAnnotation(){ System.out.println("pointCut簽名。。。"); } //前置通知 @Before("logAnnotation()") public void deBefore(JoinPoint jp) throws Throwable { // 接收到請(qǐng)求,記錄請(qǐng)求內(nèi)容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 記錄下請(qǐng)求內(nèi)容 System.out.println("URL : " + request.getRequestURL().toString()); } //返回通知 @AfterReturning(returning = "ret", pointcut = "logAnnotation()") public void doAfterReturning(Object ret) throws Throwable { // 處理完請(qǐng)求,返回內(nèi)容 System.out.println("返回通知:方法的返回值 : " + ret); } //異常通知 @AfterThrowing(throwing = "ex", pointcut = "logAnnotation()") public void throwss(JoinPoint jp,Exception ex){ System.out.println("異常通知:方法異常時(shí)執(zhí)行....."); System.out.println("產(chǎn)生異常的方法:"+jp); System.out.println("異常種類:"+ex); } //后置通知 @After("logAnnotation()") public void after(JoinPoint jp){ System.out.println("后置通知:最后且一定執(zhí)行....."); } }
Controller類
@RequestMapping("helloAnnotation") //標(biāo)有這個(gè)注解的方法會(huì)被增強(qiáng) @MyLogAnnotation(desc = "@Annotation") public Object helloAnnotation() { return "hello annotation"; }
到此這篇關(guān)于Springboot 中使用 Aop的文章就介紹到這了,更多相關(guān)Springboot使用 Aop內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決mybatis一對(duì)多關(guān)聯(lián)查詢多條數(shù)據(jù)只顯示一條的問題
這篇文章主要介紹了解決mybatis一對(duì)多關(guān)聯(lián)查詢多條數(shù)據(jù)只顯示一條的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12JavaSE API實(shí)現(xiàn)生成隨機(jī)數(shù)的2種方法(Random類和Math類的Random方法)
本文主要介紹了JavaSE API實(shí)現(xiàn)生成隨機(jī)數(shù)的2種方法,主要包括Random類和Math類的random方法都可以用來生成隨機(jī)數(shù),具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10Java實(shí)現(xiàn)插入排序算法可視化的示例代碼
插入排序的算法描述是一種簡單直觀的排序算法。其原理是通過構(gòu)建有序序列,對(duì)于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。本文將用Java語言實(shí)現(xiàn)插入排序算法并進(jìn)行可視化,感興趣的可以了解一下2022-08-08Springboot與vue實(shí)現(xiàn)文件導(dǎo)入方法具體介紹
文件導(dǎo)入時(shí)大多數(shù)項(xiàng)目無法回避的問題,這兩天深入學(xué)習(xí)了文件導(dǎo)入,在這里進(jìn)行記錄,使用到的技術(shù)是Springboot+Vue,前端組件使用el-upload2023-02-02連續(xù)調(diào)用多個(gè)外部系統(tǒng)寫接口保證數(shù)據(jù)一致性的思路
今天小編就為大家分享一篇關(guān)于連續(xù)調(diào)用多個(gè)外部系統(tǒng)寫接口保證數(shù)據(jù)一致性的思路,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12Java UUID 五個(gè)版本的區(qū)別及使用場景小結(jié)
在Java中,UUID是一個(gè)128位的唯一標(biāo)識(shí)符,廣泛應(yīng)用于生成唯一標(biāo)識(shí)符、分布式系統(tǒng)唯一鍵等場景,Java提供的java.util.UUID類支持五種UUID版本,每種具有不同的生成方式和使用場景,本文就來介紹一下如何使用,感興趣的可以了解一下2024-11-11springboot實(shí)現(xiàn)請(qǐng)求參數(shù)驗(yàn)證的多種方法
在日常開發(fā)中,我們少不了需要對(duì)前端的請(qǐng)求參數(shù)的驗(yàn)證,Spring提供了多種方法來實(shí)現(xiàn)請(qǐng)求參數(shù)的驗(yàn)證,文中通過代碼示例給大家講解的非常詳細(xì),我們一起了解一下吧2023-11-11