您好,登錄后才能下訂單哦!
這篇文章主要講解了“Spring AOP的概念及用法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Spring AOP的概念及用法”吧!
什么是AOP?
AOP術語
通知(Advice)
連接點(Join point)
切點(Pointcut)
連接點和切點的區別
切面(Aspect)
引入(Introduction)
織入(Weaving)
SpringAOP
SpringAOP的特點
SpringBoot集成SpringAOP
- 依賴引入
- 創建注解
- 定義切面
- 設置切點
- 業務接口編寫
- 測試
通知時機
- 正常情況
- 異常情況
AOP,即我們平時經常提到的面向切面編程。首先我們要理解一個叫橫切關注點(cross-cutting concern)的概念,它其實是描述我們應用中的功能,假如有一個功能,它在應用程序中很多個地方都用了,那么我們把這樣的功能稱之為橫切關注點。
日常開發中,我們都會將不同的業務場景抽象出對應的模塊進行開發,而不同的模塊,除了那些針對特定領域的核心功能外,還有一些相同的輔助功能,比如日志管理、安全管理、事務管理等等。橫切關注點這個概念其實就點明了:類似這樣的功能就是我們面向切面編程需要關注的地方。這也是面向切面編程的意義所在:它幫助我們實現橫切關注點和他們所影響的對象之間的解耦。
面向切面編程的實質,就是講橫切關注點模塊化成稱為切面的特殊的類。
以下討論都基于SpringAOP
切面的工作被稱為通知。也就是定義了切面的要做什么,以及何時做的問題。下面是Spring切面有5種類型的通知,以及相對應的在SpringBoot中的五個注解
前置通知(Before):方法調用之前,對應@Before
后置通知(After):方法調用之后(不關心方法輸出是什么)對應@After
返回通知(After-returning):目標方法成功執行之后,對應@AfterReturning
異常通知(After-throwing):目標方法拋出異常之后,對應@AfterThrowing
環繞通知(Around):方法調用之前和之后,對應@Around
后置通知和返回通知的區別
后置通知應用時機在返回通知之后,任何情況下都會應用,而返回通知只有方法正常執行
正常返回后執行
環繞通知與其它通知的區別
不同于其它的通知,環繞通知有目標方法的執行權,能夠控制目標方法是否執行。而其它的通知更多是對目標方法的一個增強,無法影響目標方法的執行
以上兩點,我們下面會通過一個例子更好的體會其中的差異。
程序中那些我們想要應用通知的地方,就是連接點。這個點可以是我們調用方法時、拋出異常時或甚至是修改某一個字段的時候。切面代碼(通知)可以通過這些點插入到應用的正常流程中,是原本的功能增添新的行為。
我們的應用程序可能會有很多個連接點需要我們應用通知,所以我們有必要把連接點的分類匯總,抽象出相同的特點,好讓正確的切面切入到正確的地方去,各司其職,而不是切入所有的連接點。切點定義 了一個切面需要在哪里進行切入。是一堆具有特定切面切入需求的連接點的共性抽象。
我們通常通過明確類和方法名、或者匹配正則表達式的方式來指定切點
切點是對的擁有相同特點的連接點集合的一個抽象。切點定義了連接點的特性,是一個描述性的歸納,由于在SpringAOP中切點的切入級別是方法,所以那些符合切點定義的方法,都是一個連接點,連接點是切點的具象表現。
切面是通知和切點的結合。通知和切點共同定了切面的全部內容——它想要干什么,在何時何地完成功能。
引入能夠讓我們在不修改原有類的代碼的基礎上,添加生的方法或屬性。
織入是把切面應用到目標對象并創建新的代理對象的過程。
SpringAOP是通過動態代理實現的。不同于AspectJ等其他aop框架,SpringAOP只支持方法級別的切點,不支持類構造器或字段級別的切點。因此只能攔截方法進行通知,而在對象創建或對象變量的修改時,都無法應用通知
SpringBoot引入AOP依賴后,spring.aop.auto屬性默認是開啟的,也就是說只要引入了AOP依賴后,默認已經增加了@EnableAspectJAutoProxy
現在我們來設想一個場景:我有若干個業務場景,每個場景對應一個服務,有些服務需要我在調服務的時候,打印開始和結束信息,并輸入服務處理的時長。
首先在項目中引入SpringAOP的依賴:
<!--引入AOP依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
創建一個自定義注解@ApiLog
,用boolean類型的字段來決定是否開啟日志輸入功能,代碼如下:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ApiLog { boolean isOn() default true; }
定義一個切面類MyAspect.java
,代碼如下
@Aspect @Component public class MyAspect { @Pointcut("execution(* com.acelin.hello.study.springTest.aop.AspectController.*(..))") private void onePointcut(){} @Around("onePointcut()") public Object around(ProceedingJoinPoint point)throws Throwable{ Signature signature = point.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); /* 獲取方法上的注解 */ ApiLog apiLog = method.getAnnotation(ApiLog.class); if (apiLog != null && apiLog.isOn()){ System.out.println("ApiLog is on!"); } Object[] objects = point.getArgs(); String bussinessName = (String) objects[0]; System.out.println("-- " + bussinessName + " start --"); long begin = System.currentTimeMillis(); Object object = point.proceed(); long end = System.currentTimeMillis(); System.out.println("-- " + bussinessName + " end 耗時" + (end - begin) + "ms --"); return object; } }
@Pointcut("execution(* com.acelin.hello.study.springTest.aop.AspectController.*(..))") private void onePointcut(){}
@Pointcut
注解表示聲明一個切點,然后我們要配置execution指示器,它用來匹配所有符合條件的連接點。已上面的指示器配置* com.acelin.hello.study.springTest.aop.AspectController.*(..)
為例,分析一下指示器的寫法
第一個*
號:表示的不關心方法的返回值類型
中間的com.acelin.hello.study.springTest.aop.AspectController.*
指定方法的特點
第二個*
號:表示AspectController類下的任意方法
而(..)
:表示不關心參數個數和類型
我們寫一個簡單的接口,然后注釋我們自定義的注解,開啟相關日志的輸出。
@RestController @RequestMapping("/aop") public class AspectController implements IAspect{ @RequestMapping("/test/1/{businessName}") @ApiLog(isOn = true) public void testAop(@PathVariable("businessName") String businessName){ System.out.println("This is a controller about aop test!"); } }
測試結果如下:
ApiLog is on!
-- 業務1 start --
This is a controller about aop test!
-- 業務1 end 耗時5ms --
以上我們通過一個簡單的例子,初步體驗了aop的魅力。接下來我們來討論以下各類通知的特點以及應用時機問題。我們對的上面的切面類進行了修改,加上所有類型的通知。
@Aspect @Component public class MyAspect { @Pointcut("execution(* com.acelin.hello.study.springTest.aop.AspectController.*(..))") private void onePointcut(){} @Before("onePointcut()") public void before(){ System.out.println("-- before"); } @After("onePointcut()") public void after(JoinPoint joinPoint) throws NoSuchMethodException{ System.out.println("-- after"); } @AfterThrowing("onePointcut()") public void afterThrowing(){ System.out.println("-- afterThrowing"); } @AfterReturning("onePointcut()") public void afterReturning(){ System.out.println("-- afterReturning"); } @Around("onePointcut()") public Object around(ProceedingJoinPoint point)throws Throwable{ Signature signature = point.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); /* 獲取方法上的注解 */ ApiLog apiLog = method.getAnnotation(ApiLog.class); if (apiLog != null && apiLog.isOn()){ System.out.println("ApiLog is on!"); } Object[] objects = point.getArgs(); String bussinessName = (String) objects[0]; System.out.println("-- " + bussinessName + " start"); long begin = System.currentTimeMillis(); Object object = point.proceed(); long end = System.currentTimeMillis(); System.out.println("-- " + bussinessName + " end 耗時" + (end - begin) + "ms"); return object; } }
然后修改接口,怎加業務名稱的一個長度判斷,如果名稱太長,拋出異常,來模擬方法執行過程中法僧異常
@RequestMapping("/test/1/{businessName}") @ApiLog public void testAop(@PathVariable("businessName") String businessName){ System.out.println("This is a controller about aop test!"); if (businessName.length() > 4){ throw new RuntimeException("業務名稱太長!"); } }
來看一下當方法正常執行的情況下,前置通知、后置通知、返回通知和環繞通知的應用時機。
再次調用測試接口,可看到如下結果:
ApiLog is on!
-- 業務 start
-- before
This is a controller about aop test!
-- afterReturning
-- after
-- 業務 end 耗時6ms
然后我們修改業務名稱為,使其超過規定長度導致觸發異常拋出,結果如下:
ApiLog is on!
-- 超級復雜的業務 start
-- before
This is a controller about aop test!
-- afterThrowing
-- afterjava.lang.RuntimeException: 業務名稱太長!
at com.acelin.hello.study.springTest.aop.AspectController.testAop(AspectController.java:16)
從上面結果可以看出的,前置通知應用于目標方法執行前,然后當方法正常執行時,會在方法結束前執行返回通知,如果發生異常,執行異常通知,返回通知不執行(因為方法已經中斷,不正常返回),而后置通知的是不管方法執行情況,都會在方法結束后執行。環繞通知則包裹著以上所有流程。
感謝各位的閱讀,以上就是“Spring AOP的概念及用法”的內容了,經過本文的學習后,相信大家對Spring AOP的概念及用法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。