zoukankan      html  css  js  c++  java
  • Spring(十八):Spring AOP(二):通知(前置、后置、返回、异常、环绕)

    AspectJ支持5种类型的通知注解:

    • @Before:前置通知,在方法执行之前执行;
    • @After:后置通知,在方法执行之后执行;
    • @AfterRunning:返回通知,在方法返回结果之后执行(因此该通知方法在方法抛出异常时,不能执行);
    • @AfterThrowing:异常通知,在方法抛出异常之后执行;
    • @Around:环绕通知,围绕着方法执行。

    示例项目新建:

    第一步:新建spring aop项目

    第二步:添加spring-aop.xml Spring配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.dx.spring.beans.aop"></context:component-scan>
        <!-- 配置是AspectJ注解起作用 :自动为匹配的类生成代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    View Code

    第三步:添加接口类IArithmeticCalculator.java,ArithmeticCalculatorImpl.java Spring组件

    package com.dx.spring.beans.aop;
    
    /**
     * Description:Addition, subtraction, multiplication, and division
     */
    public interface IArithmeticCalculator {
        int add(int i, int j);
    
        int sub(int i, int j);
    
        int multi(int i, int j);
    
        int div(int i, int j);
    }
    View Code
    package com.dx.spring.beans.aop;
    
    import org.springframework.stereotype.Component;
    
    @Component("arithmeticCalculator")
    public class ArithmeticCalculatorImpl implements IArithmeticCalculator {
        @Override
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        @Override
        public int multi(int i, int j) {
            int result = i * j;
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    }
    View Code

    第四步:添加LoggingAspect.java切面

    package com.dx.spring.beans.aop;
    
    import java.util.List;
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。
    @Aspect
    @Component
    public class LoggingAspect {
        // 在这里注册通知方法。
    }

    第五步:添加测试类Client.java

    package com.dx.spring.beans.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Client {
        public static void main(String[] args) {
            // 1:创建Spring的IOC容器;
            ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
            // 2.从IOC容器中获取Bean的实例
            IArithmeticCalculator arithmeticCalculator = (IArithmeticCalculator) ctx.getBean("arithmeticCalculator");
            // 3.使用Bean
    
    
        }
    }

    @Before:前置通知

    在切面类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);
        }

    Client.java添加测试代码&执行测试:

            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
    
            result = arithmeticCalculator.div(4, 1);
            System.out.println(result);

    before method add with [1, 3]
    before method div with [4, 0]

    @After:后置通知

    在切面类LoggingAspect中添加后置通知方法:

        // 声明该方法为一个后置通知:在目标方法结束之后执行(无论方法是否抛出异常)。
        // 但是因为当方法抛出异常时,不能返回值为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);
        }

    Client.java添加测试代码&执行测试:

            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
    
            result = arithmeticCalculator.div(4, 0);
            System.out.println(result);

    执行结果:

    备注:从执行结果中我们可以看出,即使方法抛出异常,后置通知方法也会执行。

    @AfterRunning:返回通知

     修改切面类LoggingAspect,添加返回通知方法:

        /**
         * 声明该方法为一个返回通知:在目标方法返回结果时后执行(当方法抛出异常时,无法执行)。
         * 但是因为当方法抛出异常时,类似代码
         * {@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()));
        }

    注意:返回通知注解中,参数是多一个了returning参数,该参数定义通知方法接收返回值的别名。

    在测试类Client.java中追加测试代码:

            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
    
            result = arithmeticCalculator.div(4, 0);
            System.out.println(result);

    @AfterThrowing:异常通知

     修改切面类LoggingAspect,添加异常通知方法:

        /**
         * 定义一个异常通知函数:
         * 只有在方法抛出异常时,该方法才会执行,而且可以接受异常对象。
         * */
        @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()));
        }

    备注:只有在方法抛出异常时,异常通知方法才会执行,而且可以接受异常对象。

    在测试类Client.java中追加测试代码:

            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
    
            result = arithmeticCalculator.div(4, 0);
            System.out.println(result);

    打印信息

    @Around:环绕通知

    环绕通知其他方式不同,它相当于代理对象使用时效果一样。可以在方法前、后、返回、异常打印信息。

    我们先看代理方法打印信息时如何处理,之后再看环绕通知如何处理。

    代理方法实现通知:

    package com.dx.spring.beans.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ArithmeticCalculatorProxy implements InvocationHandler {
        private Object obj;
    
        public ArithmeticCalculatorProxy(Object obj) {
            this.obj = obj;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object object = null;
            try {
                // *** 前置通知,在方法执行之前执行
                System.out.println("before " + method);
    
                // 真实的调用方法操作
                object = method.invoke(obj, args);
    
                // *** 返回通知,在方法返回结果之后执行(因此该通知方法在方法抛出异常时,不能执行);
                System.out.println("before returning " + method);
            } catch (Exception ex) {
                ex.printStackTrace();
                // *** 异常通知,在方法抛出异常之后执行;
                System.out.println("before throwing " + method);
                throw ex;
            }
            // *** 后置通知,在方法执行之后执行;因为方法可以抛出异常,所以访问不到方法的返回值
            System.out.println("after " + method);
    
            return object;
        }
    }

    环绕通知:

    修改切面类LoggingAspect,添加环绕通知:

    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 intcom.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 intcom.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 intcom.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;
        }
    }

    修改Client.java测试类,添加代码:

            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
    
            result = arithmeticCalculator.div(4, 0);
            System.out.println(result);

    执行结果

  • 相关阅读:
    选择器的使用总结
    突出展示
    京东轮播图的·实现方式
    jquery实现下拉菜单
    jquery实现淘宝动态图展示商品
    jquery查找子元素和兄弟元素
    调制解调
    同步异步串行并行
    Quartus ii 初学遇到的问题以及解决
    PLL
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/9129612.html
Copyright © 2011-2022 走看看