上一篇文章《Spring AOP 面向切面編程入門》對AOP作了簡要的介紹,包含一些專業術語的解釋。
本文基於SpringBoot編寫了一個簡單的Spring AOPDemo。
maven依賴添加如下
<!--引入SpringBoot的Web模塊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入AOP依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
注意:在完成了引入AOP依賴包后,不需要去做其他配置。AOP的默認配置屬性中,spring.aop.auto屬性默認是開啟的,也就是說只要引入了AOP依賴后,默認已經增加了@EnableAspectJAutoProxy,不需要在程序主類中增加@EnableAspectJAutoProxy來啟用。
web請求入口:對應系統縱向的核心業務模塊。
package com.example.demo.aop; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @desc: 核心業務模塊 * @author: CSH **/ @RestController @RequestMapping("/aopController") public class AopController { @RequestMapping(value = "/Curry") public void Curry(){ System.out.println("庫里上場打球了!!"); } @RequestMapping(value = "/Harden") public void Harden(){ System.out.println("哈登上場打球了!!"); } @RequestMapping(value = "/Antetokounmpo") public void Antetokounmpo(){ System.out.println("字母哥上場打球了!!"); } @RequestMapping(value = "/Jokic") public void Jokic(){ System.out.println("約基奇上場打球了!!"); } @RequestMapping(value = "/Durant/{point}") public void Durant(@PathVariable("point") int point){ System.out.println("杜蘭特上場打球了!!"); } }
定義切面類:在類上添加@Aspect 和@Component 注解即可將一個類定義為切面類。
@Aspect 注解 使之成為切面類
@Component 注解 把切面類加入到IOC容器中
package com.example.demo.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @desc: 經紀人切面 * @author: CSH **/ @Aspect @Component public class BrokerAspect { /** * 定義切入點,切入點為com.example.demo.aop.AopController中的所有函數 *通過@Pointcut注解聲明頻繁使用的切點表達式 */ @Pointcut("execution(public * com.example.demo.aop.AopController.*(..)))") public void BrokerAspect(){ } /** * @description 在連接點執行之前執行的通知 */ @Before("BrokerAspect()") public void doBeforeGame(){ System.out.println("經紀人正在處理球星賽前事務!"); } /** * @description 在連接點執行之后執行的通知(返回通知和異常通知的異常) */ @After("BrokerAspect()") public void doAfterGame(){ System.out.println("經紀人為球星表現瘋狂鼓掌!"); } /** * @description 在連接點執行之后執行的通知(返回通知) */ @AfterReturning("BrokerAspect()") public void doAfterReturningGame(){ System.out.println("返回通知:經紀人為球星表現瘋狂鼓掌!"); } /** * @description 在連接點執行之后執行的通知(異常通知) */ @AfterThrowing("BrokerAspect()") public void doAfterThrowingGame(){ System.out.println("異常通知:球迷要求退票!"); } }
調用服務
http://localhost:8080/aopController/Harden,輸出結果:
切點表達式用於描述切點的位置信息,在此簡單描述文中切點表達式的含義。
推薦一個切點表達式總結的博客
https://www.cnblogs.com/zhangxufeng/p/9160869.html
這里單獨講解一下功能強大的環繞通知,環繞通知可以將你所編寫的邏輯將被通知的目標方法完全包裝起來。我們可以使用一個環繞通知來代替之前多個不同的前置通知和后置通知。如下所示,前置通知和后置通知位於同一個方法中,不像之前那樣分散在不同的通知方法里面。
/** * @description 使用環繞通知 */ @Around("BrokerAspect()") public void doAroundGame(ProceedingJoinPoint pjp) throws Throwable { try{ System.out.println("經紀人正在處理球星賽前事務!"); pjp.proceed(); System.out.println("返回通知:經紀人為球星表現瘋狂鼓掌!"); } catch(Throwable e){ System.out.println("異常通知:球迷要求退票!"); } }
環繞通知接受ProceedingJoinPoint作為參數,它來調用被通知的方法。通知方法中可以做任何的事情,當要將控制權交給被通知的方法時,需要調用ProceedingJoinPoint的proceed()方法。當你不調用proceed()方法時,將會阻塞被通知方法的訪問。
當通知方法需要傳入參數我們又該怎樣處理呢?
和之前創建的切面一樣,這里的不同點在於切點還聲明了要提供給通知方法的參數。
切點表達式args(point)表明傳遞給GameDataAspect()方法中的int類型參數也會傳遞到通知中去,參數名point和缺點方法簽名中的參數相匹配
package com.example.demo.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @desc:技術統計 * @author: CSH **/ @Aspect @Component public class GameDataAspect { /** * 定義切入點,切入點為com.example.demo.aop.AopController中的所有函數 *通過@Pointcut注解聲明頻繁使用的切點表達式 */ @Pointcut("execution(public * com.example.demo.aop.AopController.Durant(int)) && args(point))") public void GameDataAspect(int point){ } /** * @description 使用環繞通知 */ @Around("GameDataAspect(point)") public void doAroundGameData(ProceedingJoinPoint pjp,int point) throws Throwable { try{ System.out.println("球星上場前熱身!"); pjp.proceed(); System.out.println("球星本場得到" + point + "分" ); } catch(Throwable e){ System.out.println("異常通知:球迷要求退票!"); } } }
本文代碼Github鏈接
https://github.com/LemonFive/SpringBootAopDemo
