zoukankan      html  css  js  c++  java
  • Spring AOP 学习(五)

    1. 使用动态代理实现AOP

    package com.atguigu.spring.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    public class ArithmeticCalculatorLoggingProxy {
        
        //要代理的对象
        private ArithmeticCalculator target;
        
        public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
            super();
            this.target = target;
        }
    
        //返回代理对象
        public ArithmeticCalculator getLoggingProxy(){
            ArithmeticCalculator proxy = null;
            
            ClassLoader loader = target.getClass().getClassLoader();
            Class [] interfaces = new Class[]{ArithmeticCalculator.class};
            InvocationHandler h = new InvocationHandler() {
                /**
                 * proxy: 代理对象。 一般不使用该对象
                 * method: 正在被调用的方法
                 * args: 调用方法传入的参数
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    String methodName = method.getName();
                    //打印日志
                    System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
                    
                    //调用目标方法
                    Object result = null;
                    
                    try {
                        //前置通知
                        result = method.invoke(target, args);
                        //返回通知, 可以访问到方法的返回值
                    } catch (NullPointerException e) {
                        e.printStackTrace();
                        //异常通知, 可以访问到方法出现的异常
                    }
                    
                    //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
                    
                    //打印日志
                    System.out.println("[after] The method ends with " + result);
                    
                    return result;
                }
            };
            
            /**
             * loader: 代理对象使用的类加载器。 
             * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法. 
             * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
             */
            proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
            
            return proxy;
        }
    }
    AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
    AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
    在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
    AOP 的好处:
    每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
    业务模块更简洁, 只包含核心业务代码.

     JoinPoint 连接点 指在哪个点触发 某个具体的方法前或者方法后或者发生异常时。

    切面: 需要执行的方法的类 @AspectJ

    通知:需要执行的方法

    在方法被调用之前需要生成动态代理对象

    当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理

     通知的5种类型

    @Before

    @After

    @AfterReturning

    @AfterThrowing

    @Around 类似于动态代理 相当于其他几个的综合功能

    package com.atguigu.spring.aop;
    
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
     * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
     */
    @Order(2)
    @Aspect
    @Component
    public class LoggingAspect {
        
        /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
         * 使用 @Pointcut 来声明切入点表达式. 
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
         */
        @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
        
        /**
         * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
         */
        @Before("declareJointPointExpression()")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            Object [] args = joinPoint.getArgs();
            
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }
        
        /**
         * 在方法执行之后执行的代码. 无论该方法是否出现异常
         */
        @After("declareJointPointExpression()")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends");
        }
        
        /**
         * 在方法法正常结束受执行的代码
         * 返回通知是可以访问到方法的返回值的!
         */
        @AfterReturning(value="declareJointPointExpression()",
                returning="result")
        public void afterReturning(JoinPoint joinPoint, Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends with " + result);
        }
        
        /**
         * 在目标方法出现异常时会执行的代码.
         * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
         */
        @AfterThrowing(value="declareJointPointExpression()",
                throwing="e")
        public void afterThrowing(JoinPoint joinPoint, Exception e){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " occurs excetion:" + e);
        }
        
        /**
         * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
         * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
         * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
         */
        /*
        @Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public Object aroundMethod(ProceedingJoinPoint pjd){
            
            Object result = null;
            String methodName = pjd.getSignature().getName();
            
            try {
                //前置通知
                System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
                //执行目标方法
                result = pjd.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;
        }
        */
    }

    @Order 注解指定切面的优先级

    @Pointcut

    /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
         * 使用 @Pointcut 来声明切入点表达式. 
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
         */
        @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
  • 相关阅读:
    linux内核剖析(六)Linux系统调用详解(实现机制分析)
    Linux内核剖析(五)Linux内核的构建过程
    Linux内核剖析(四)为arm内核构建源码树
    Linux内核剖析(三)构建源码树
    Linux内核剖析(二)Linux内核绪论
    kubectl更新镜像和回滚命令
    Linux登录shell和非登录(交互式shell)环境变量配置
    Elasticsearch7.6学习笔记1 Getting start with Elasticsearch
    docker安装Elasticsearch7.6集群并设置密码
    Jenkinsfile里定义对象和函数,获取git提交人, 发送钉钉通知
  • 原文地址:https://www.cnblogs.com/mengjianzhou/p/9131604.html
Copyright © 2011-2022 走看看