基于springboot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的aop實(shí)例
簡(jiǎn)介
AOP(Aspect-Oriented Programming:面向切面編程)
aop能將一些繁瑣、重復(fù)、無關(guān)業(yè)務(wù)的邏輯封裝起來,在一個(gè)地方進(jìn)行統(tǒng)一處理,常用于日志記錄、事務(wù)管理、權(quán)限控制等,aop能在不改變?cè)写a邏輯的基礎(chǔ)上對(duì)某個(gè)方法、某類方法、或者整個(gè)類進(jìn)行無侵入式的加強(qiáng),有效降低了代碼耦合度,并且提高了項(xiàng)目擴(kuò)展性;
ok廢話說完,進(jìn)入正題,如何實(shí)現(xiàn)一個(gè)aop
要實(shí)現(xiàn)aop,首先你要知道你拿aop來干啥,我們今天就以記錄日志來說,因?yàn)檫@個(gè)最常用,一般對(duì)于重要的數(shù)據(jù)庫(kù)操作,我們需要記錄操作人、什么時(shí)間、做了什么,關(guān)于做了什么怎么實(shí)現(xiàn)我們后面細(xì)講(要想知道做了什么,肯定得知道是哪個(gè)方法、并且哪些參數(shù),這些屬于進(jìn)階操作,我們先簡(jiǎn)單實(shí)現(xiàn)一個(gè)aop)
我們先new一個(gè)切面
@Aspect @Component public class LogAspect { @Pointcut("execution(* com.example.mydemos.controller..*(..))") public void controllerMenthod() { } @Before("controllerPointcut()") public void beforeExecute() { System.out.println("before..."); } @After("controllerPointcut()") public void afterExecute() { System.out.println("after..."); } }
關(guān)于注解
- @Aspect:告訴spring這是一個(gè)切面;
- @Component:將切面交由spring來管理;
- @Pointcut:切入點(diǎn),直白點(diǎn)就是指定你需要從哪個(gè)地方切入,再直白點(diǎn)就是你想增強(qiáng)的目標(biāo)方法,這里需要了解下execution表達(dá)式,可以通過這里來指定你需要切入的方法,可以指定單個(gè)方法、整個(gè)類的所有方法、類的某些方法、整個(gè)包下所有類的所有方法等;
- @Before:目標(biāo)方法執(zhí)行前需要做的事;
- @After:目標(biāo)方法執(zhí)行后需要做的事
還有幾個(gè)常用注解:
@Around(能自由的指定在目標(biāo)方法執(zhí)行前后做增強(qiáng)邏輯,需要手動(dòng)調(diào)用ProceedingJoinPoint的proceed方法來執(zhí)行目標(biāo)方法,不調(diào)用則目標(biāo)方法不會(huì)執(zhí)行,如果目標(biāo)方法有返回值,還需手動(dòng)返回)
@AfterReturning(在目標(biāo)方法正常執(zhí)行完成后做增強(qiáng),如果你需要獲取方法返回值就用它)
@AfterThrowing(當(dāng)目標(biāo)方法執(zhí)行過程中拋出異常時(shí)執(zhí)行)
執(zhí)行時(shí)機(jī):
切入目標(biāo)方法時(shí),先織入Around,再織入Before,退出目標(biāo)方法時(shí),先織入Around,再織入AfterReturning,最后才織入After
來個(gè)測(cè)試controller
就是個(gè)平平無奇的普通controller
@RestController public class HiController { @GetMapping("/hi") public String sayHello() { System.out.println("hi, good morning~"); return "hi bro ~"; } }
我這個(gè)controller是放在Pointcut對(duì)應(yīng)com.example.mydemos.controller包下的,所以該包下的所有類的所有方法都會(huì)被增強(qiáng)
先假設(shè)后驗(yàn)證
按照上述demo
當(dāng)我訪問"/hi"時(shí),會(huì)先執(zhí)行@Before對(duì)應(yīng)方法,輸出"before…",再執(zhí)行HiController 中的sayHello方法,輸出"hi, good morning~",并且返回"hi bro ~",最后執(zhí)行@After對(duì)應(yīng)方法,輸出"after…"
驗(yàn)證:
項(xiàng)目跑起來訪問"/hi"
控制臺(tái)
驗(yàn)證成功~
一個(gè)最基礎(chǔ)的aop實(shí)現(xiàn)完畢,接下來搞點(diǎn)進(jìn)階操作
獲取目標(biāo)方法參數(shù)
再來個(gè)測(cè)試controller
@RestController public class HelloController { @GetMapping("/hello/{title}/{content}") public String sayHello(@PathVariable("title") String title, @PathVariable("content") String content) { System.out.println(title + ":" + content); return "hello ya~"; } }
現(xiàn)在我們有兩個(gè)controller,順便能測(cè)試下execution規(guī)則是否生效,我的規(guī)則是com.example.mydemos.controller下的所有方法都增強(qiáng)
HelloController的sayHello方法有兩個(gè)參數(shù)title和content,看我們能不能拿到
獲取目標(biāo)方法參數(shù)需要用到JoinPoint,經(jīng)測(cè)試,在@Before和@After中均能獲取
@Before("controllerPointcut()") public void beforeExecute(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); List<Object> list = Arrays.asList(args); System.out.println("before中的目標(biāo)方法參數(shù)"); list.forEach(System.out::println); System.out.println("before..."); } @After("controllerPointcut()") public void afterExecute(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); List<Object> list = Arrays.asList(args); System.out.println("after中的目標(biāo)方法參數(shù)"); list.forEach(System.out::println); System.out.println("after..."); }
joinPoint.getArgs()會(huì)返回一個(gè)object數(shù)組,這就是你的目標(biāo)方法參數(shù)
測(cè)試
結(jié)果
獲取目標(biāo)方法名
所有符合規(guī)則的方法都會(huì)被增強(qiáng),那我怎么知道當(dāng)前執(zhí)行的是哪個(gè)方法呢?
@Before("controllerPointcut()") public void beforeExecute(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println("before中的方法名:"+name); System.out.println("before..."); } @After("controllerPointcut()") public void afterExecute(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println("after中的方法名:"+name); System.out.println("after..."); }
joinPoint.getSignature().getName()返回的就是方法名
獲取目標(biāo)方法返回值
這個(gè)就需要用到@Around或者@AfterReturning
一、@Around
@Around("controllerPointcut()") public Object aruondExecute(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around before..."); String name = joinPoint.getSignature().getName(); Object o = joinPoint.proceed(); System.out.println("方法" + name + "的返回值是" + o); System.out.println("around after..."); return o; }
注意,如果用around,需手動(dòng)調(diào)用ProceedingJoinPoint.proceed才能執(zhí)行目標(biāo)方法,并且如果目標(biāo)方法有返回值,需要手動(dòng)return
訪問"/hi"
二、@AfterReturning
@AfterReturning(value = "controllerPointcut()", returning = "result") public void AfterReturningExecute(JoinPoint joinPoint, Object result) { System.out.println("AfterReturning..."); String name = joinPoint.getSignature().getName(); System.out.println("方法" + name + "的返回值是" + result); }
用AfterReturning的話需要添加一個(gè)參數(shù)returning,用于接收返回值,且AfterReturning注解中的形參要和AfterReturningExecute中的一致,不然識(shí)別不到
訪問"/hi"
文中demo已上傳至gitee
順便求個(gè)star
么么嘰~
到此這篇關(guān)于基于springboot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的aop的文章就介紹到這了,更多相關(guān)springboot 實(shí)現(xiàn)aop內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot使用AOP與注解實(shí)現(xiàn)請(qǐng)求參數(shù)自動(dòng)填充流程詳解
- Spring?AOP實(shí)現(xiàn)用戶登錄統(tǒng)一驗(yàn)證功能
- SpringBoot使用AOP實(shí)現(xiàn)統(tǒng)計(jì)全局接口訪問次數(shù)詳解
- spring AOP實(shí)現(xiàn)@Around輸出請(qǐng)求參數(shù)和返回參數(shù)
- spring aop底層原理及如何實(shí)現(xiàn)
- SpringAop實(shí)現(xiàn)操作日志記錄
- Spring AOP 的組成和實(shí)現(xiàn)
相關(guān)文章
Java try-catch-finally異常處理機(jī)制詳解
這篇文章主要介紹了Java try-catch-finally異常處理機(jī)制詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08java中不定長(zhǎng)參數(shù)的實(shí)例用法
在本篇文章里小編給大家分享的是關(guān)于java中不定長(zhǎng)參數(shù)的使用方法以及相關(guān)代碼內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。2020-02-02JAVA基于Slack實(shí)現(xiàn)異常日志報(bào)警詳解
這篇文章主要為大家介紹了JAVA基于Slack實(shí)現(xiàn)異常日志報(bào)警詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Java設(shè)計(jì)模式之Iterator模式介紹
所謂Iterator模式,即是Iterator為不同的容器提供一個(gè)統(tǒng)一的訪問方式。本文以java中的容器為例,模擬Iterator的原理。需要的朋友可以參考下2013-07-07mybatis?log4j2打印sql+日志實(shí)例代碼
在學(xué)習(xí)mybatis的時(shí)候,如果用log4j2來協(xié)助查看調(diào)試信息,則會(huì)大大提高學(xué)習(xí)的效率,加快debug速度,下面這篇文章主要給大家介紹了關(guān)于mybatis?log4j2打印sql+日志的相關(guān)資料,需要的朋友可以參考下2022-08-08Java不指定長(zhǎng)度的二維數(shù)組實(shí)例
今天小編就為大家分享一篇Java不指定長(zhǎng)度的二維數(shù)組實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07java 中ThreadLocal本地線程和同步機(jī)制的比較
這篇文章主要介紹了java 中ThreadLocal本地線程和同步機(jī)制的比較的相關(guān)資料,需要的朋友可以參考下2017-03-03