zoukankan      html  css  js  c++  java
  • Spring AOP详解

    Spring AOP简介

    AOP(Aspect Oriented Programming)把软件系统分为两个部分:核心关注点和横切关注点

    • 核心关注点:业务处理的主要流程
    • 横切关注点:特点是经常发生在核心关注点的多处,而各处基本相似

    典型应用场景:权限认证、日志

    需求

    • 1.实现一个计算器接口,需要有计算加减乘除的方法
    • 2.为了便于日后核查问题,需要在日志总记录每次计算的入参及计算结果

    基本接口

    public interface ArithmeticCalculator {
        int add(int i, int j);
    
        int sub(int i, int j);
    
        int mul(int i, int j);
    
        int div(int i, int j);
    }
    

    基本接口的实现

    public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        public int mul(int i, int j) {
            int result = i * j;
            return result;
        }
    
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    }
    

    版本1--2B青年写法

    手动撸代码,下下策

    public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    
        public int add(int i, int j) {
            System.out.println("The method add begins with [" + i + ", " + j + "]");
            int result = i + j;
            System.out.println("The method add end with " + result);
            return result;
        }
    
        public int sub(int i, int j) {
            System.out.println("The method sub begins with [" + i + ", " + j + "]");
            int result = i - j;
            System.out.println("The method sub end with " + result);
            return result;
        }
    
        public int mul(int i, int j) {
            System.out.println("The method mul begins with [" + i + ", " + j + "]");
            int result = i * j;
            System.out.println("The method  mul end with " + result);
            return result;
        }
    
        public int div(int i, int j) {
            System.out.println("The method div begins with [" + i + ", " + j + "]");
            int result = i / j;
            System.out.println("The method div end with " + result);
            return result;
        }
    }
    

    版本2--文艺青年写法(代理模式)

    基于代理模式的实现增加了一个代理类,在代理类中增加了前置方法和后置方法,不失为一种轻量级解决方案。

    public class ArithmeticCaculatorProxy implements ArithmeticCalculator {
        //要代理的对象
        private ArithmeticCalculator arithmeticCalculator;
    
        public ArithmeticCaculatorProxy(ArithmeticCalculator arithmeticCalculator){
            this.arithmeticCalculator = arithmeticCalculator;
        }
    
        private void before(int ... args) {
            System.out.println("The method add begins with " + Arrays.toString(args));
        }
    
        private void after(int ... args) {
            System.out.println("The method add ends with " + Arrays.toString(args));
        }
    
        public int add(int i, int j) {
            before(i, j);
            int result = arithmeticCalculator.add(i, j);
            after(result);
            return result;
        }
    
        public int sub(int i, int j) {
            before(i, j);
            int result = arithmeticCalculator.sub(i, j);
            after(result);
            return result;
        }
    
        public int mul(int i, int j) {
            before(i, j);
            int result = arithmeticCalculator.mul(i, j);
            after(result);
            return result;
        }
    
        public int div(int i, int j) {
            before(i, j);
            int result = arithmeticCalculator.div(i, j);
            after(result);
            return result;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
            ArithmeticCaculatorProxy proxy = new ArithmeticCaculatorProxy(arithmeticCalculator);
            proxy.add(1, 5);
            System.out.println("----------");
            proxy.sub(5, 3);
            System.out.println("----------");
            proxy.mul(3, 7);
            System.out.println("----------");
            proxy.div(9, 3);
        }
    }
    

    版本3--装B青年写法(动态代理)

    这种方式简单粗暴,直接对代理对象下手。

    public class ArithmeticCaculatorProxy {
        //要代理的对象
        private ArithmeticCalculator target;
    
        public ArithmeticCaculatorProxy(ArithmeticCalculator target){
            this.target = target;
        }
    
        public ArithmeticCalculator getProxy() {
            ArithmeticCalculator proxy = null;
            ClassLoader classLoader = target.getClass().getClassLoader();
            Class[] interfaces = new Class[]{ArithmeticCalculator.class};
            InvocationHandler handler = new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String methodName = method.getName();
                    System.out.println("The method " + methodName + " begins with" + Arrays.asList(args));
                    System.out.println("Invoke...");
                    //执行方法
                    Object result = method.invoke(target, args);
                    //日志
                    System.out.println("The method " + methodName + " ends with " + result);
                    return result;
                }
            };
            proxy = (ArithmeticCalculator) Proxy.newProxyInstance(classLoader, interfaces, handler);
            return proxy;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            ArithmeticCalculator target = new ArithmeticCalculatorImpl();
            ArithmeticCalculator proxy = new ArithmeticCaculatorProxy(target).getProxy();
            proxy.add(1, 5);
            System.out.println("----------");
            proxy.sub(5, 3);
            System.out.println("----------");
            proxy.mul(3, 7);
            System.out.println("----------");
            proxy.div(9, 3);
        }
    }
    

    版本4--武林高手写法之一(Spring AOP注解)

    package com.umgsai.aop.level4;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * Created by shangyidong on 17/6/29.
     */
    //指定切面的优先级,当有多个切面时,数值越小优先级越高
    @Order(1)
    //把这个类声明为一个切面:需要把该类放入到IOC容器中。再声明为一个切面.
    @Aspect
    @Component
    public class LoggingAspect {
        /**
         * 声明切入点表达式,一般在该方法中不再添加其他代码。
         * 使用@Pointcut来声明切入点表达式。
         * 后面的通知直接使用方法名来引用当前的切入点表达式。
         */
        @Pointcut("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))")
        public void declareJoinPointExpression() {}
    
        /**
         *前置通知,在目标方法开始之前执行。
         *@Before("execution(public int com.spring.aop.impl.ArithmeticCalculator.add(int, int))")这样写可以指定特定的方法。
         * @param joinpoint
         */
        @Before("declareJoinPointExpression()")
        //这里使用切入点表达式即可。后面的可以都改成切入点表达式。如果这个切入点表达式在别的包中,在前面加上包名和类名即可。
        public void beforeMethod(JoinPoint joinpoint) {
            String methodName = joinpoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinpoint.getArgs());
            System.out.println("前置通知:The method "+ methodName +" begins with " + args);
        }
    
        /**
         *后置通知,在目标方法执行之后开始执行,无论目标方法是否抛出异常。
         *在后置通知中不能访问目标方法执行的结果。
         * @param joinpoint
         */
        @After("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(int, int))")
        public void afterMethod(JoinPoint joinpoint) {
            String methodName = joinpoint.getSignature().getName();
            //List<Object>args = Arrays.asList(joinpoint.getArgs());  后置通知方法中可以获取到参数
            System.out.println("后置通知:The method "+ methodName +" ends ");
        }
    
        /**
         *返回通知,在方法正常结束之后执行。
         *可以访问到方法的返回值。
         * @param joinpoint
         * @param result 目标方法的返回值
         */
        @AfterReturning(value="execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))", returning="result")
        public void afterReturnning(JoinPoint joinpoint, Object result) {
            String methodName = joinpoint.getSignature().getName();
            System.out.println("返回通知:The method "+ methodName +" ends with " + result);
        }
    
        /**
         *异常通知。目标方法出现异常的时候执行,可以访问到异常对象,可以指定在出现特定异常时才执行。
         *假如把参数写成NullPointerException则只在出现空指针异常的时候执行。
         * @param joinpoint
         * @param e
         */
        @AfterThrowing(value="execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))", throwing="e")
        public void afterThrowing(JoinPoint joinpoint, Exception e) {
            String methodName = joinpoint.getSignature().getName();
            System.out.println("异常通知:The method "+ methodName +" occurs exception " + e);
        }
    
        /**
         * 环绕通知类似于动态代理的全过程,ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
         * @param point 环绕通知需要携带ProceedingJoinPoint类型的参数。
         * @return 目标方法的返回值。必须有返回值。
         */
         /*不常用
        @Around("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))")
        public Object aroundMethod(ProceedingJoinPoint point) {
            Object result = null;
            String methodName = point.getSignature().getName();
            try {
                //前置通知
                System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs()));
                //执行目标方法
                result = point.proceed();
                //翻译通知
                System.out.println("The method "+ methodName +" ends with " + result);
            } catch (Throwable e) {
                //异常通知
                System.out.println("The method "+ methodName +" occurs exception " + e);
                throw new RuntimeException(e);
            }
            //后置通知
            System.out.println("The method "+ methodName +" ends");
            return result;
        }
        */
    }
    
    public class Main {
        public static void main(String[] args) {
            //创建spring IOC容器
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-level4.xml");
            //从IOC容器中获取bean实例
            ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class);
            int result = arithmeticCalculator.add(4, 6);
            System.out.println(result);
            result = arithmeticCalculator.sub(4, 6);
            System.out.println(result);
            result = arithmeticCalculator.mul(4, 6);
            System.out.println(result);
            result = arithmeticCalculator.div(4, 0);
            System.out.println(result);
        }
    }
    

    此时需要在Spring的配置文件中加入以下注解

        <!-- 使AspectJ注解起作用:自动为匹配的类生产代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    Spring AOP注解参数详解
    • @Aspect 声明切面类,放入IOC容器中
    • @Order 指定切面的优先级,当有多个切面时,数值越小优先级越高
    • @Pointcut 声明切入点
    • @Before 前置通知
    • @After 后置通知
    • @AfterReturning 返回通知
    • @AfterThrowing 异常通知
    • @Around 环绕通知

    版本5--武林高手写法之二(Spring AOP配置)

    @Component("loggingAspect")
    public class LoggingAspect {
    
        public void beforeMethod(JoinPoint joinpoint) {
            String methodName = joinpoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinpoint.getArgs());
            System.out.println("前置通知:The method "+ methodName +" begins with " + args);
        }
    
        public void afterMethod(JoinPoint joinpoint) {
            String methodName = joinpoint.getSignature().getName();
            //List<Object>args = Arrays.asList(joinpoint.getArgs());  后置通知方法中可以获取到参数
            System.out.println("后置通知:The method "+ methodName +" ends ");
        }
    
        public void afterReturning(JoinPoint joinpoint, Object result) {
            String methodName = joinpoint.getSignature().getName();
            System.out.println("返回通知:The method "+ methodName +" ends with " + result);
        }
    
        public void afterThrowing(JoinPoint joinpoint, Exception e) {
            String methodName = joinpoint.getSignature().getName();
            System.out.println("异常通知:The method "+ methodName +" occurs exception " + e);
        }
    
        public Object aroundMethod(ProceedingJoinPoint point) {
            Object result = null;
            String methodName = point.getSignature().getName();
            try {
                //前置通知
                System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs()));
                //执行目标方法
                result = point.proceed();
                //翻译通知
                System.out.println("The method "+ methodName +" ends with " + result);
            } catch (Throwable e) {
                //异常通知
                System.out.println("The method "+ methodName +" occurs exception " + e);
                throw new RuntimeException(e);
            }
            //后置通知
            System.out.println("The method "+ methodName +" ends");
            return result;
        }
    }
    
    @Component("validationAspect")
    public class ValidationAspect {
        public void validateArgs(JoinPoint joinPoint) {
            System.out.println("validate:" + Arrays.asList(joinPoint.getArgs()));
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            //创建spring IOC容器
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-level5.xml");
            //从IOC容器中获取bean实例
            ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class);
            int result = arithmeticCalculator.add(4, 6);
            System.out.println(result);
            result = arithmeticCalculator.sub(4, 6);
            System.out.println(result);
            result = arithmeticCalculator.mul(4, 6);
            System.out.println(result);
            //result = arithmeticCalculator.div(4, 0);
            //System.out.println(result);
        }
    }
    

    xml配置文件如下

        <!-- 配置AOP -->
        <aop:config>
            <!-- 配置切点表达式 -->
            <aop:pointcut expression="execution(* com.umgsai.aop.level5.ArithmeticCalculator.*(..))" id="pointcut"/>
            <!-- 配置切面及通知,使用order指定优先级 -->
            <aop:aspect ref="loggingAspect" order="1">
                <!-- 环绕通知 -->
                <!--
                    <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
                -->
                <!-- 前置通知 -->
                <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
                <!-- 后置通知 -->
                <aop:after method="afterMethod" pointcut-ref="pointcut"/>
                <!-- 异常通知 -->
                <aop:after-throwing method="afterThrowing"  pointcut-ref="pointcut" throwing="e"/>
                <!-- 返回通知 -->
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
    
            </aop:aspect>
            <aop:aspect ref="validationAspect" order="2">
                <!-- 前置通知 -->
                <aop:before method="validateArgs" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    

    这里有两个切面类,通过配置文件中的order即可控制优先级.

    项目源码: http://git.oschina.net/umgsai/spring-aop

  • 相关阅读:
    各种blog尝试后,发现wordpress适用起来最方便
    索引器(C# 编程指南)
    vue3 父子组件之间的传值
    vue3 + vite + ts 搭建项目
    vue3中使用全局变量
    用JSONKit库解析json文件
    程序内部让用户直接上appstore评价游戏的链接地址以及跳转方法
    常见错误:Apple MachO Linker Error
    常见错误:多线程界面元素显示错误
    常见错误:Undefined symbols for architecture i386
  • 原文地址:https://www.cnblogs.com/umgsai/p/7131606.html
Copyright © 2011-2022 走看看