spring AOP AspectJ 定義切面實現攔截

總結記錄一下AOP經常使用的應用場景及使用方式,若有錯誤,請留言.java

 

1.  講AOP以前,先來總結web項目的幾種攔截方式web

     A:  過濾器 正則表達式

            使用過濾器能夠過濾URL請求,以及請求和響應的信息,可是過濾器是隻是針對J2EE規範實現的,沒法判斷ServletRequest請求是由哪一個controller方法處理spring

      B:  攔截器編程

            攔截器能夠獲取到URL的請求和響應信息,以及處理請求的controller方法信息,可是沒法獲取方法的參數,要使用spring 提供的攔截器,具體的作法以下:緩存

            a. 實現spring 提供的攔截器接口tomcat

            b. 實現攔截器接口的3個方法,方法的參數 handler 實際上就是處理請求的 controller的方法聲明, 可強轉爲 HandlerMethod 對象,HandlerMethod對象能夠獲得方法所在控制器類名,方法名稱,可是沒法獲取方法的參數安全

               --若是想獲取參數信息,使用切面Aspect(spring核心功能 AOP的實現)性能優化

           ps: 攔截器會攔截全部控制器的方法的調用,不光是咱們本身寫的,spring 框架提供的控制器也會被攔截框架

      C:  切面 

           切面能獲取處處理請求的方法,方法所屬的類實例,方法的返回值、全類名、參數類型,方法的上註解等信息

           ps:  spring AOP 是基於 切面(AspectJ) 實現的

    關於以上幾種攔截方式執行順序的說明:

           過濾器優先於攔截器執行,攔截器優先於切面執行,可是晚於切面結束,最後過濾器晚於攔截器結束

    具體順序以下:

            過濾器DoFilter方法開始->攔截器preHandle方法開始執行->切面方法開始->controller方法->切面方法結束->攔截器postHandle,afterCompletion方法開始執行,執行後攔截器結束->過濾器DoFilter方法結束

    程序出現異常的處理順序:

           若是 controller方法 出現異常,最早捕獲到異常的是的切面,若是切面拋出異常,由控制器異常處理器處理,若是控制器異常處理器不處理,異常會拋到攔截器,若是攔截器未處理,異常會拋到過濾器,若是過濾器未處理會拋到tomcat返回給用戶

 

2.  關於AOP的介紹

    有一篇博客已經介紹的很詳細了  這裏直接引用過來,你們也能夠訪問原地址獲取更多:   https://blog.csdn.net/wuruijiang/article/details/78970720

    -- 什麼是AOP

         AOP(Aspect-OrientedProgramming,面向方面編程),能夠說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構,用以模擬公共行爲的一個集合。當咱們須要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。也就是說,OOP容許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其餘類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼,在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。

      而AOP技術則偏偏相反,它利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即方面。所謂「方面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。AOP表明的是一個橫向的關係,若是說「對象」是一個空心的圓柱體,其中封裝的是對象的屬性和行爲;那麼面向方面編程的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以得到其內部的消息。而剖開的切面,也就是所謂的「方面」了。而後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。

        使用「橫切」技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特色是,他們常常發生在覈心關注點的多處,而各處都基本類似。好比權限認證、日誌、事務處理。Aop 的做用在於分離系統中的各類關注點,將核心關注點和橫切關注點分離開來。正如Avanade公司的高級方案構架師Adam Magee所說,AOP的核心思想就是「將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。」

         實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼。

      --   AOP使用場景

AOP用來封裝橫切關注點,具體能夠在下面的場景中使用:

 Authentication 權限

Caching 緩存

Context passing 內容傳遞

Error handling 錯誤處理

Lazy loading 懶加載

Debugging  調試

logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準

Performance optimization 性能優化

Persistence  持久化

Resource pooling 資源池

Synchronization 同步

Transactions 事務

      --  AOP相關概念

方面(Aspect):一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用spring的 Advisor或攔截器實現。

鏈接點(Joinpoint): 程序執行過程當中明確的點,如方法的調用或特定的異常被拋出。

通知(Advice): 在特定的鏈接點,AOP框架執行的動做。各類類型的通知包括「around」、「before」和「throws」通知。通知類型將在下面討論。許多AOP框架包括Spring都是以攔截器作通知模型,維護一個「圍繞」鏈接點的攔截器鏈。Spring中定義了四個advice:                                               BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

 切入點(Pointcut): 指定一個通知將被引起的一系列鏈接點的集合。AOP框架必須容許開發者指定切入點:例如,使用正則表達式。 Spring定義了Pointcut接口,用來組合MethodMatcher和ClassFilter,能夠經過名字很清楚的理解, MethodMatcher是用來檢查目標類的                                  方法是否能夠被應用此通知,而ClassFilter是用來檢查Pointcut是否應該應用到目標類上

 引入(Introduction): 添加方法或字段到被通知的類。 Spring容許引入新的接口到任何被通知的對象。例如,你可使用一個引入使任何對象實現 IsModified接口,來簡化緩存。Spring中要使用Introduction, 可有經過DelegatingIntroductionInterceptor來實現通知,經過                                        DefaultIntroductionAdvisor來配置Advice和代理類要實現的接口

 目標對象(Target Object): 包含鏈接點的對象。也被稱做被通知或被代理對象。POJO

 AOP代理(AOP Proxy): AOP框架建立的對象,包含通知。 在Spring中,AOP代理能夠是JDK動態代理或者CGLIB代理。

 織入(Weaving): 組裝方面來建立一個被通知對象。這能夠在編譯時完成(例如使用AspectJ編譯器),也能夠在運行時完成。Spring和其餘純Java AOP框架同樣,在運行時完成織入。

 

3. spring AOP的一些註解及API 說明

 在開發中一般咱們會使用 AspectJ 註解實現.

  A:  經常使用的切面有如下幾種,它決定了切入方法的位置: 

       前置:@Before

       後置:@After

       返回值:@AfterReturing

       異常:@AfterThrowing

        環繞:@Around

  B:  鏈接點

       JoinPoint、ProceedJoinPoint 鏈接點 其實就是切面表達式覆蓋的方法,根據該鏈接點能夠獲取多個信息,例如 處理請求的方法,方法所屬的類實例,方法的返回值、全類名、參數類型,方法的上註解等信息,經常使用的方法以下:

              getSignature():獲取鏈接點方法的返回值、全類名、參數類型

              getTarget():獲取鏈接點方法所屬的類實例

              getArgs():獲取鏈接點方法的參數列表

              getThis() :獲取代理對象自己

       ProceedingJoinPoint繼承JoinPoint子接口,它新增了兩個用於執行鏈接點方法的方法 : 

              proceed() :  經過反射執行目標對象的鏈接點處的方法

              proceed(java.lang.Object[] args) :   經過反射執行目標對象鏈接點處的方法,不過使用新的入參替換原來的入參

       JoinPoint 通常用在除了@Around 註解的方法上,ProceedJoinPoint 通常用在@Around 中(由於須要使用 ProceedJoinPoint 的 proceed()方法進行目標方法的執行)

  B:  切點表達式

        切點表達式就是方法(鏈接點)的匹配表達式,用於說明切點要匹配的方法路徑:

        例如 :  * com.qxl.web.controller.userController.*(..)    

                    表示此方法在 com.qxl.web.controller.userController下的任何方法,方法包含任何參數,任何返回值 的方法上都能執行

                    第一個  * :  表示方法任意權限,返回值爲任意值都能執行此加強邏輯

                    第二個  * :  userController 下的任何方法都能執行此加強邏輯

                    (..)   :  表示方法裏的參數爲任意值 (不管參數多少,大小,類型)均可以執行此加強邏輯

 

3. spring AOP 的具體例子, 具體可見代碼

    

package com.qxl.web.aspect; import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import com.qxl.web.controller.userController; /** * 使用切面實現url攔截: * 切片的實現分爲2個步 A: 切入點(使用 @Before @After @Around 等註解實現 : 用於表示 在那些方法上(註解參數)起做用 ,在何時(註解類型)起做用) * B: 加強 (起做用的時候執行的業務邏輯) * 具體以下: * 1. 定義一個類,添加 @Aspect 註解,代表這是一個切片類,同時加上@Component註解將這個類在spring容器中聲明 * 2. 在你的加強邏輯的方法上使用 @Before @After @Around 等註解,能夠傳遞參數 ProceedingJoinPoint 對象, * ProceedingJoinPoint對象相似於攔截器的 handler,可獲取加強的方法的信息,類的名稱和方法名稱以及方法參數等等 * 3. 編寫邏輯代碼完善要加強方法的邏輯,以下的handleControllerMethod方法 * * @author qxl * */ @Aspect // spring 切面類須要添加此註解
@Component public class AspectDemo { //@Before //在要加強方法以前執行 //@After //在要加強方法以後執行
    /** * 在要加強方法上環繞執行(先後均可以執行) * 註解參數: execution(* com.qxl.web.controller.userController.*(..)) * 表示此方法在 com.qxl.web.controller.userController下的任何方法,方法包含任何參數,任何返回值 的方法上都能執行 * 第一個 * : 表示方法返回值爲任意值都能執行此加強邏輯 * 第二個 * : userController 下的任何方法都能執行此加強邏輯 * (..) : 表示方法裏的參數爲任意值 (不管參數多少,大小,類型)均可以執行此加強邏輯 * @throws Throwable */ @Around("execution(* com.qxl.web.controller.userController.*(..))") public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("Aspect start"); //getTarget() 得到鏈接點所屬類的實例
        Object target = pjp.getTarget(); if (target instanceof userController) { System.out.println("當前鏈接點方法是userController類中的方法"); } //獲得鏈接點方法的參數
        Object[] args = pjp.getArgs(); for (Object arg : args) { System.out.println("arg is "+ arg); } //getSignature():signature是信號,標識的意思,獲取被加強的方法相關信息
        Signature signature = pjp.getSignature(); //獲得方法名稱
        System.out.println("Aspect 攔截到了" + signature.getName() +"方法..."); //獲得方法所在的包名
        System.out.println("方法所在的包名:"+ signature.getDeclaringTypeName()); //獲得方法上的註解--若是 方法上存在一個 @Permission 註解
        MethodSignature methodSignature = (MethodSignature)signature; Method method = methodSignature.getMethod(); if(method.isAnnotationPresent(Permission.class)){ //獲得這個註解
            Permission permission = method.getAnnotation(Permission.class); //.....other operation
 } //獲取方法參數類型
        Class<?>[] parameterTypes = method.getParameterTypes(); // 獲得方法的返回值類型
        Class<?> returnType = method.getReturnType(); //proceed()方法用來調用ProceedingJoinPoint對象獲取到的那個的方法(即執行切片要加強的那個方法),proceed()方法返回的Object就是加強方法的返回值 // 若是proceed()方法傳遞了參數,會替換原來方法的參數
        Object object = pjp.proceed(); System.out.println("Aspect end"); return object; } }
相關文章
相關標籤/搜索