記錄一次簡單的aop學習歷程:java
先介紹一些aop的名詞,其實這些名詞對使用aop沒什麼影響,但爲了更好的理解最好知道一些數組
切面(Aspect):一個關注點的模塊化,這個關注點可能會橫切多個對象。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面能夠使用基於模式或者基於@Aspect註解的方式來實現。緩存
鏈接點(Joinpoint):在程序執行過程當中某個特定的點,好比某方法調用的時候或者處理異常的時候。在Spring AOP中,一個鏈接點老是表示一個方法的執行。session
通知(Advice):在切面的某個特定的鏈接點上執行的動做。其中包括了「around」、「before」和「after」等不一樣類型的通知(通知的類型將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器作通知模型,並維護一個以鏈接點爲中心的攔截器鏈。app
切入點(Pointcut):匹配鏈接點的斷言。通知和一個切入點表達式關聯,並在知足這個切入點的鏈接點上運行(例如,當執行某個特定名稱的方法時)。切入點表達式如何和鏈接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。框架
引入(Introduction):用來給一個類型聲明額外的方法或屬性(也被稱爲鏈接類型聲明(inter-type declaration))。Spring容許引入新的接口(以及一個對應的實現)到任何被代理的對象。例如,你能夠使用引入來使一個bean實現IsModified接口,以便簡化緩存機制。模塊化
目標對象(Target Object):被一個或者多個切面所通知的對象。也被稱作被通知(advised)對象。既然Spring AOP是經過運行時代理實現的,這個對象永遠是一個被代理(proxied)對象。學習
AOP代理(AOP Proxy):AOP框架建立的對象,用來實現切面契約(例如通知方法執行等等)。在Spring中,AOP代理能夠是JDK動態代理或者CGLIB代理。測試
織入(Weaving):把切面鏈接到其它的應用程序類型或者對象上,並建立一個被通知的對象。這些能夠在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。Spring和其餘純Java AOP框架同樣,在運行時完成織入。this
切入點表達式的格式:execution([可見性] 返回類型 [聲明類型].方法名(參數) [異常])
本次採用註解的方式來使用,須在aop類上配置註解,並配置component來使類能夠被掃描到
@Aspect @Component public class TestAop {
主要有@Before,@After,@AfterReturning,@Around,@AfterThrowing這幾個註解
controller類代碼段以下
@RestController public class TestController { @RequestMapping("/heihei") public String heihei(){ System.out.println("執行方法/heihei"); return "heihei"; } @RequestMapping("/haha") public String haha(){ System.out.println("執行方法/haha"); return "哈哈"; } @RequestMapping("/test") public String test(){ System.out.println("執行方法/test"); return "test測試"; } }
AOP類代碼段以下
@Aspect @Component public class TestAop { //在執行方法以前執行 @Before("execution(* com.example.controller.*.*(..))") public void before(){ System.out.println("執行以前"); } //在return以後開始執行,在@after以後執行,返回參數沒用,因此返回類型爲void 不返回參數 //入參爲方法執行後返回的結果 若配置了@Around 入參rvt爲加強以後的結果 @AfterReturning(pointcut = "execution(* com.example.controller.*.*(..))",returning = "rvt" ) public void afterRetrun(JoinPoint jPoint,Object rvt){ //rvt 便是方法返回的值 System.out.println(rvt.toString()+"AfterReturning"); // 返回被織入加強處理的目標方法 System.out.println("AfterReturning加強:被織入加強處理的目標方法爲:" + jPoint.getSignature().getName()); // 訪問執行目標方法的參數 System.out.println("AfterReturning加強:目標方法的參數爲:" + Arrays.toString(jPoint.getArgs())); // 訪問被加強處理的目標對象 System.out.println("AfterReturning加強:被織入加強處理的目標對象爲:" + jPoint.getTarget()); } /* * 當定義一個Around加強處理方法時,該方法的第一個參數必須是ProceedingJoinPoint類型(至少包含一個形參), * 在加強處理方法體內,調用ProceedingJoinPoint的proceed()方法纔會執行目標方法—— * 這就是Around加強處理能夠徹底控制目標方法執行時機、如何執行的關鍵; * 若是程序沒有調用ProceedingJoinPoint的proceed()方法,則目標方法不會被執行。 * 調用ProceedingJoinPoint的proceed()方法時,還能夠傳入一個Object[]對象,該數組中的值將被傳入目標方法做爲執行方法的實參。 * 只有本方法返回值纔有意義,返回類型纔有必要不是void * 除了@Around外,每一個方法裏均可以加或者不加參數JoinPoint,若是有用JoinPoint的地方就加, * 不加也能夠,JoinPoint裏包含了類名、被切面的方法名,參數等屬性,可供讀取使用。 * @Around參數必須爲ProceedingJoinPoint,pjp.proceed相應於執行被切面的方法。 * @AfterReturning方法裏,能夠加returning = 「XXX」,XXX即爲在controller裏方法的返回值。 * @AfterThrowing方法裏,能夠加throwing = "XXX",供讀取異常信息。 * */ @Around("execution(* com.example.controller.*.*(..))") public String around(ProceedingJoinPoint pjp){ //獲取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); //從獲取RequestAttributes中獲取HttpServletRequest的信息 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //若是要獲取Session信息的話,能夠這樣寫: //HttpSession session = (HttpSession) // requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION); System.out.println("環繞方法"); try { Object proceed = pjp.proceed(); System.out.println(proceed.toString()); } catch (Throwable throwable) { throwable.printStackTrace(); } /*System.out.println("環繞方法的對象是"+this.getClass().toString());*/ return "around"; } @AfterThrowing(throwing = "ex", pointcut = "execution(* com.example.controller.*.*(..))") public void doRecoveryActions(Throwable ex) { System.out.println("目標方法中拋出的異常:" + ex); System.out.println("模擬拋出異常後的加強處理..."); } //在執行方法以後執行 @After("execution(* com.example.controller.*.*(..))") public void release(JoinPoint jp) { System.out.println("after 模擬方法結束後的釋放資源..."); Object[] args = jp.getArgs(); String s = Arrays.toString(args); System.out.println("方法參數爲"+s); // 被返回織入加強處理的目標方法 System.out.println("After加強:被織入加強處理的目標方法爲:" + jp.getSignature().getName()); // 訪問執行目標方法的參數 System.out.println("After加強:目標方法的參數爲:" + Arrays.toString(jp.getArgs())); // 訪問被加強處理的目標對象 System.out.println("After加強:被織入加強處理的目標對象爲:" + jp.getTarget()); } }
運行結果以下
環繞方法 執行以前 執行方法/test test測試 環繞方法的對象是class com.example.aop.TestAop after 模擬方法結束後的釋放資源... 方法參數爲[] After加強:被織入加強處理的目標方法爲:test After加強:目標方法的參數爲:[] After加強:被織入加強處理的目標對象爲:com.example.controller.TestController@38aa512b aroundAfterReturning AfterReturning加強:被織入加強處理的目標方法爲:test AfterReturning加強:目標方法的參數爲:[] AfterReturning加強:被織入加強處理的目標對象爲:com.example.controller.TestController@38aa512b
@Around能夠直接控制原方法會不會執行,若不執行Object proceed = pjp.proceed(),則原方法不會執行,這樣@After等註解的方法也不會運行起做用,@Around裏面的返回值就是最終的返回值。@Around修飾的方法裏面能夠沒有參數,即不執行原方法,直接返回值,此種方式沒有意義。通常傳參數ProceedingJoinPoint用來執行原方法。
對於後置返回通知 這裏須要注意的是: 若是參數中的第一個參數爲JoinPoint,則第二個參數爲返回值的信息 若是參數中的第一個參數不爲JoinPoint,則第一個參數爲returning中對應的參數 returning 限定了只有目標方法返回值與通知方法相應參數類型時才能執行後置返回通知,不然不執行,對於returning對應的通知方法參數爲Object類型將匹配任何目標返回值。上述紅字不匹配可能報錯