Spring AOP 簡單應用,對請求參數進行攔截處理


AOP的主要角色

  • 切面:使用切點表達式表示,指定了當前切面邏輯所要包裹的業務模塊的范圍大小;
  • Advice:也即切面邏輯,指定了當前用於包裹切面指定的業務模塊的邏輯

Advice的主要類型

  • @Before:該注解標注的方法在業務模塊代碼執行之前執行,其不能阻止業務模塊的執行,除非拋出異常;
  • @AfterReturning:該注解標注的方法在業務模塊代碼執行之后執行;
  • @AfterThrowing:該注解標注的方法在業務模塊拋出指定異常后執行;
  • @After:該注解標注的方法在所有的Advice執行完成后執行,無論業務模塊是否拋出異常,類似於finally的作用;
  • @Around:該注解功能最為強大,其所標注的方法用於編寫包裹業務模塊執行的代碼,其可以傳入一個ProceedingJoinPoint用於調用業務模塊的代碼,無論是調用前邏輯還是調用后邏輯,都可以在該方法中編寫,甚至其可以根據一定的條件而阻斷業務模塊的調用;
  • @DeclareParents:其是一種Introduction類型的模型,在屬性聲明上使用,主要用於為指定的業務模塊添加新的接口和相應的實現。
  • @Aspect:嚴格來說,其不屬於一種Advice,該注解主要用在類聲明上,指明當前類是一個組織了切面邏輯的類,並且該注解中可以指定當前類是何種實例化方式,主要有三種:singleton、perthis和pertarget,具體的使用方式后面會進行講解。

        這里需要說明的是,@Before是業務邏輯執行前執行,與其對應的是@AfterReturning,而不是@After,@After是所有的切面邏輯執行完之后才會執行,無論是否拋出異常。

 切點表達式

   由於Spring切面粒度最小是達到方法級別,而execution表達式可以用於明確指定方法返回類型,類名,方法名和參數名等與方法相關的部件,並且在Spring中,大部分需要使用AOP的業務場景也只需要達到方法級別即可,因而execution表達式的使用是最為廣泛的。如下是execution表達式的語法:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

       這里問號表示當前項可以有也可以沒有,其中各項的語義如下:

  • modifiers-pattern:方法的可見性,如public,protected;
  • ret-type-pattern:方法的返回值類型,如int,void等;
  • declaring-type-pattern:方法所在類的全路徑名,如com.spring.Aspect;
  • name-pattern:方法名類型,如buisinessService();
  • param-pattern:方法的參數類型,如java.lang.String;
  • throws-pattern:方法拋出的異常類型,如java.lang.Exception;

        如下是一個使用execution表達式的例子:

execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))

   上述切點表達式將會匹配使用public修飾,返回值為任意類型,並且是com.spring.BusinessObject類中名稱為businessService的方法,方法可以有多個參數,但是第一個參數必須是java.lang.String類型的方法。上述示例中我們使用了..通配符,關於通配符的類型,主要有兩種:

  • *通配符,該通配符主要用於匹配單個單詞,或者是以某個詞為前綴或后綴的單詞。

  如下示例表示返回值為任意類型,在com.spring.service.BusinessObject類中,並且參數個數為零的方法:

execution(* com.spring.service.BusinessObject.*())

下述示例表示返回值為任意類型,在com.spring.service包中,以Business為前綴的類,並且是類中參數個數為零方法:

execution(* com.spring.service.Business*.*())
  • ..通配符,該通配符表示0個或多個項,主要用於declaring-type-pattern和param-pattern中,如果用於declaring-type-pattern中,則表示匹配當前包及其子包,如果用於param-pattern中,則表示匹配0個或多個參數。

       如下示例表示匹配返回值為任意類型,並且是com.spring.service包及其子包下的任意類的名稱為businessService的方法,而且該方法不能有任何參數:

execution(* com.spring.service..*.businessService())

 這里需要說明的是,包路徑service..*.businessService()中的..應該理解為延續前面的service路徑,表示到service路徑為止,或者繼續延續service路徑,從而包括其子包路徑;后面的*.businessService(),這里的*表示匹配一個單詞,因為是在方法名前,因而表示匹配任意的類。

       如下示例是使用..表示任意個數的參數的示例,需要注意,表示參數的時候可以在括號中事先指定某些類型的參數,而其余的參數則由..進行匹配:

execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))

接下來看一個實例

@Configuration
@Aspect
public class ServiceAspect {

    private final String ExpGetResultDataPonit = "execution(public * com.mystudy.spring.api.add*(..))";


    @Pointcut(ExpGetResultDataPonit)
    public void checkParam() {
    }

    @Around("checkParam()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = ((MethodSignature) proceedingJoinPoint.getSignature());
        System.out.println("環繞通知的目標方法名:" + signature.getName());


        //獲取參數值
        Object[] paranValues = proceedingJoinPoint.getArgs();

        for (Object arg : paranValues) {
            System.out.println("ARG原來為:" + arg);
            if (arg instanceof User) {
                User u = (User) arg;
                u.setUserName("李四");
            }
        }

        return proceedingJoinPoint.proceed();

    }



}

參考:

https://www.cnblogs.com/zhangxufeng/p/9160869.html
https://blog.csdn.net/puhaiyang/article/details/78146620
https://yq.aliyun.com/articles/652998

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM