AOP入門

什么是AOP?

AOP英文全稱:Aspect Oriented Programming(面向切面編程、面向方面編程),其實說白了,面向切面編程就是面向特定方法編程。AOP面向方法編程,就可以做到在不改動這些原始方法的基礎(chǔ)上,針對特定的方法進行功能的增強。AOP的作用:在程序運行期間在不修改源代碼的基礎(chǔ)上對已有方法進行增強(無侵入性: 解耦)

中間運行的原始業(yè)務(wù)方法,可能是其中的一個業(yè)務(wù)方法,比如:我們只想通過 部門管理的 list 方法的執(zhí)行耗時,那就只有這一個方法是原始業(yè)務(wù)方法。 而如果,我們是先想統(tǒng)計所有部門管理的業(yè)務(wù)方法執(zhí)行耗時,那此時,所有的部門管理的業(yè)務(wù)方法都是 原始業(yè)務(wù)方法。 那面向這樣的指定的一個或多個方法進行編程,我們就稱之為 面向切面編程。

AOP的優(yōu)勢:減少重復(fù)代碼,提高開發(fā)效率,維護方便

AOP快速入門

需求:統(tǒng)計各個業(yè)務(wù)層方法執(zhí)行耗時。

實現(xiàn)步驟:

1.導(dǎo)入依賴:在pom.xml中導(dǎo)入AOP的依賴

2.編寫AOP程序:針對于特定方法根據(jù)業(yè)務(wù)需要進行編程

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

AOP程序:TimeAspect

@Component

@Aspect //當(dāng)前類為切面類

@Slf4j

public class TimeAspect {

? ? @Around("execution(* com.itheima.service.*.*(..))")

? ? public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {

? ? ? ? //記錄方法執(zhí)行開始時間

? ? ? ? long begin = System.currentTimeMillis();

? ? ? ? //執(zhí)行原始方法

? ? ? ? Object result = pjp.proceed();

? ? ? ? //記錄方法執(zhí)行結(jié)束時間

? ? ? ? long end = System.currentTimeMillis();

? ? ? ? //計算方法執(zhí)行耗時

? ? ? ? log.info(pjp.getSignature()+"執(zhí)行耗時: {}毫秒",end-begin);

? ? ? ? return result;

? ? }

}

總結(jié):其實AOP常見的應(yīng)用場景如下:

1.記錄系統(tǒng)的操作日志

2.權(quán)限控制

3.事務(wù)管理:我們前面所講解的Spring事務(wù)管理,底層其實也是通過AOP來實現(xiàn)的,只要添加@Transactional注解之后,AOP程序自動會在原始方法運行前先來開啟事務(wù),在原始方法運行完畢之后提交或回滾事務(wù)

AOP進階

通知類型,通知順序,切入點表達式,連接點

@Around:環(huán)繞通知,此注解標注的通知方法在目標方法前、后都被執(zhí)行

@Before:前置通知,此注解標注的通知方法在目標方法前被執(zhí)行

@After :后置通知,此注解標注的通知方法在目標方法后被執(zhí)行,無論是否有異常都會執(zhí)行

@AfterReturning : 返回后通知,此注解標注的通知方法在目標方法后被執(zhí)行,有異常不會執(zhí)行

@AfterThrowing : 異常后通知,此注解標注的通知方法發(fā)生異常后執(zhí)行

@Around環(huán)繞通知需要自己調(diào)用 ProceedingJoinPoint.proceed() 來讓原始方法執(zhí)行,其他通知不需要考慮目標方法執(zhí)行

@Around環(huán)繞通知方法的返回值,必須指定為Object,來接收原始方法的返回值,否則原始方法執(zhí)行完畢,是獲取不到返回值的。

Spring提供了@PointCut注解,該注解的作用是將公共的切入點表達式抽取出來,需要用到時引用該切入點表達式即可。

@Slf4j

@Component

@Aspect

public class MyAspect1 {

? ? //切入點方法(公共的切入點表達式)

? ? @Pointcut("execution(* com.itheima.service.*.*(..))")

? ? private void pt(){

? ? }

? ? //前置通知(引用切入點)

? ? @Before("pt()")

? ? public void before(JoinPoint joinPoint){

? ? ? ? log.info("before ...");

? ? }

? ? //環(huán)繞通知

? ? @Around("pt()")

? ? public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

? ? ? ? log.info("around before ...");

? ? ? ? //調(diào)用目標對象的原始方法執(zhí)行

? ? ? ? Object result = proceedingJoinPoint.proceed();

? ? ? ? //原始方法在執(zhí)行時:發(fā)生異常

? ? ? ? //后續(xù)代碼不在執(zhí)行

? ? ? ? log.info("around after ...");

? ? ? ? return result;

? ? }

? ? //后置通知

? ? @After("pt()")

? ? public void after(JoinPoint joinPoint){

? ? ? ? log.info("after ...");

? ? }

? ? //返回后通知(程序在正常執(zhí)行的情況下,會執(zhí)行的后置通知)

? ? @AfterReturning("pt()")

? ? public void afterReturning(JoinPoint joinPoint){

? ? ? ? log.info("afterReturning ...");

? ? }

? ? //異常通知(程序在出現(xiàn)異常的情況下,執(zhí)行的后置通知)

? ? @AfterThrowing("pt()")

? ? public void afterThrowing(JoinPoint joinPoint){

? ? ? ? log.info("afterThrowing ...");

? ? }

}


execution主要根據(jù)方法的返回值、包名、類名、方法名、方法參數(shù)等信息來匹配,語法為:

execution(訪問修飾符? 返回值 包名.類名.?方法名(方法參數(shù)) throws 異常?)

1.其中帶?的表示可以省略的部分

2.訪問修飾符:可省略(比如: public、protected)

3.包名.類名: 可省略

4.throws 異常:可省略(注意是方法上聲明拋出的異常,不是實際拋出的異常)

示例:@Before("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")

如果只想給查詢的方法加上,那方法取名的時候最好遵循一個規(guī)范,比如查詢的方法都是 get...,

@Before("execution(* com.itheima.service.impl.DeptServiceImpl.get*(java.lang.Integer))")

切入點表達式示例

省略方法的修飾符號

execution(voidcom.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))

使用*代替返回值類型

execution(*com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))

使用*代替包名(一層包使用一個*)

execution(*com.itheima.*.*.DeptServiceImpl.delete(java.lang.Integer))

使用..省略包名

execution(*com..DeptServiceImpl.delete(java.lang.Integer))

使用*代替類名

execution(*com..*.delete(java.lang.Integer))

使用*代替方法名

execution(*com..*.*(java.lang.Integer))

使用 * 代替參數(shù)

execution(*com.itheima.service.impl.DeptServiceImpl.delete(*))

使用..省略參數(shù)

execution(*com..*.*(..))


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容