zoukankan      html  css  js  c++  java
  • 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...");
        }
    }

    备注:如果在同一个包下,可以不指定包的路径。

    第四步:验证

  • 相关阅读:
    SQL未能排它地锁定数据库以执行该操作解决
    SQL日志文件丢失,只有MDF恢复
    ASP.Net2.0使用Log4Net(一)
    ASP.NET使用Memcached高缓存实例(初级教程)
    ASP.Net2.0使用Log4Net(二)
    Windows Server 2003域控制器的建立
    什么是SIP?
    [转].NET破解体验 ildasm.exe的使用
    Memcached深度分析(转载)
    X509证书帮助类
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/9131005.html
Copyright © 2011-2022 走看看