Spring(十九):Spring AOP(三):切面的優先級、重復使用切入點表達式


背景:

1)指定切面優先級示例:有的時候需要對一個方法指定多個切面,而這多個切面有時又需要按照不同順序執行,因此,切面執行優先級別指定功能就變得很實用。

2)重復使用切入點表達式:上一篇文章中,定義前置、后置、返回、異常通知的切入點表達式時,都使用了同一個;而且本章節新加入的驗證切面ValidateAspect類,也使用同一個切入點表達式,怎么讓他們重用呢?

指定切面優先級示例:

比如在算術計算器執行計算之前進行數據驗證,驗證完事之后才讓執行日志輸出。

新建spring aop項目參考:《Spring(十八):Spring AOP(二):通知(前置、后置、返回、異常、環繞)

添加驗證切面類:

package com.dx.spring.beans.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class ValidateAspect {    
    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))")
    public void beforeMethod() {
        System.out.println("validate...");
    }
}

除了驗證切面,還包含日志切面:

package com.dx.spring.beans.aop;

import java.util.List;
import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//把這個類聲明為一個切面:需要把該類放入到IOC容器中、再聲明為一個切面。
@Aspect
@Component
public class LoggingAspect {
    // 聲明該方法為一個前置通知:在目標方法開始之前執行
    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")
    public void beforeMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("before method " + methodName + " with " + args);
    }

    // 聲明該方法為一個后置通知:在目標方法結束之后執行(無論方法是否拋出異常)。
    // 但是因為當方法拋出異常時,不能返回值為null,因此這里無法獲取到異常值。
    @After(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))")
    public void afterMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("after method " + methodName);
    }

    /**
     * 聲明該方法為一個返回通知:在目標方法返回結果時后執行(當方法拋出異常時,無法執行)。 但是因為當方法拋出異常時,類似代碼 {@code
     * try <br/>
     * { <br/>
     * // before 前置通知 <br/>
     * // do action method <br/>
     * // after returning 返回通知,可以訪問到方法的返回值 <br/>
     * } catch(Exception e){ e.printStackTrace(); // after throwing
     * 異常通知,可以訪問到方法出現的異常 } // after 后置通知,因為方法可以拋出異常,所以訪問不到方法的返回值 }
     */
    @AfterReturning(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))", returning = "result")
    public void afterReturningMethod(JoinPoint joinpoint, Object result) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println(
                "after method " + methodName + " with returning " + (result == null ? "NULL" : result.toString()));
    }

    /**
     * 定義一個異常通知函數: 只有在方法跑吹異常時,該方法才會執行,而且可以接受異常對象。
     */
    @AfterThrowing(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinpoint, Exception ex) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println(
                "after method " + methodName + " occurs exception: " + (ex == null ? "NULL" : ex.getMessage()));
    }

//    @Around(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")
//    public Object aroundMethod(ProceedingJoinPoint pJoinPoint) throws Exception {
//        String methodName = pJoinPoint.getSignature().getName();
//        List<Object> args = Arrays.asList(pJoinPoint.getArgs());
//
//        Object result = null;
//        try {
//            // 前置通知
//            System.out.println("before method " + methodName + " with " + args);
//            // 執行目標方法
//            result = pJoinPoint.proceed();
//            // 返回通知
//            System.out.println("after method " + methodName + " returning " + result);
//        } catch (Throwable ex) {
//            // 異常通知
//            System.out.println("after method " + methodName + " occurs exception: " + ex.getMessage());
//            throw new Exception(ex);
//        }
//        // 后置通知
//        System.out.println("after method " + methodName);
//        return result;
//    }
}

默認打印結果:

按照實際業務需求來講,更希望是先執行驗證切面,才執行日志切面,那么,可以通過設置切面優先級別來設置:

修改ValidateAspect.java添加注解@Order(1)

@Order(value=1)
@Component
@Aspect
public class ValidateAspect {    
    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))")
    public void beforeMethod() {
        System.out.println("validate...");
    }
}

修改LoggingAspect.java添加注解@Order(2)

@Order(value=2)
@Aspect
@Component
public class LoggingAspect {
    //代碼
}

此時執行結果為:

重使用切入點表達式:

上一篇文章中,定義前置、后置、返回、異常通知的切入點表達式時,都使用了同一個;而且本章節新加入的驗證切面ValidateAspect類,也使用同一個切入點表達式,怎么讓他們重用呢?

第一步:修改LoggingAspect切面類,添加@Pointcut注解的方法declareAspectJoinPointExpression():

//把這個類聲明為一個切面:需要把該類放入到IOC容器中、再聲明為一個切面。
@Order(value=2)
@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))")
    public void declareAspectJoinPointExpression(){
        
    }
        //...
}

第二步:修改LoggingAspect切面類,把前置、后置、返回、異常、環繞通知方法的切入點表達式替換為“declareAspectJoinPointExpression()”

以前置通知方法為例:

//把這個類聲明為一個切面:需要把該類放入到IOC容器中、再聲明為一個切面。
@Order(value=2)
@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(..))")
    public void declareAspectJoinPointExpression(){
        
    }
    // 聲明該方法為一個前置通知:在目標方法開始之前執行
    @Before("declareAspectJoinPointExpression()")
    public void beforeMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("before method " + methodName + " with " + args);
    }
        //...    
}

第三步:修改驗證切面類ValidateAspect.java:

@Order(value=1)
@Component
@Aspect
public class ValidateAspect {    
    @Before("com.dx.spring.beans.aop.LoggingAspect.declareAspectJoinPointExpression()")
    public void beforeMethod() {
        System.out.println("validate...");
    }
}

備注:如果在同一個包下,可以不指定包的路徑。

第四步:驗證

 


免責聲明!

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



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