• Spring 详解(一)------- AOP前序



    1. AOP 简介

    ​ AOP(Aspect Oriented Programming),通常称为面向切面编程。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    2. 示例需求

    想要为写好的 ArithmeticCalculator 添加日志 ,即每次运算前后添加
    采用以下方法太过繁琐,修改内容需要每个跟着都修改,可维护性差

    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 MyArithmeticCalculatorImp 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 ends 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 ends 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 ends 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 ends with["+result+"]");
            return result;
        }
    }
    

    结果

    The method add begins with[1,2]
    The method add ends with[3]
    -->3
    The method mul begins with[5,2]
    The method mul ends with[10]
    -->10
    

    问题:
    代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还必须兼顾替他多个关注点。

    代码分散:以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码,如果日志需求发生变化,必须修改所有模块。

    3. 解决方法一:使用静态代理

    创建干净的实现类

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

    创建日志类 MyLogger

    /**
     * 创建日志类
     */
    public class MyLogger {
        /**
         * 入参日志
         * @param a
         * @param b
         */
        public void showParam(int a, int b) {
            System.out.println("The method add begins with["+a+","+b+"]");
        }
    
        /**
         * 运算结果日志
         * @param result
         */
        public void showResult(int result) {
            System.out.println("The method add ends with["+3+"]");
        }
    }
    

    创建静态代理类

    /**
     * 代理类
     */
    public class ProxyLogger implements ArithmeticCalculator {
    
        //目标类
        private ArithmeticCalculator target;
    
        //日志类
        private MyLogger logger;
    
        public ProxyLogger(ArithmeticCalculator target, MyLogger logger) {
            this.target = target;
            this.logger = logger;
        }
        @Override
        public int add(int i, int j) {
            logger.showParam(i, j);
            int result =  target.add(i,j);
            logger.showResult(result);
            return result;
        }
        @Override
        public int sub(int i, int j) {
            logger.showParam(i, j);
            int result =  target.sub(i,j);
            logger.showResult(result);
            return result;
        }
        @Override
        public int mul(int i, int j) {
            logger.showParam(i, j);
            int result =  target.mul(i,j);
            logger.showResult(result);
            return result;
        }
        @Override
        public int div(int i, int j) {
            logger.showParam(i, j);
            int result =  target.div(i,j);
            logger.showResult(result);
            return result;
        }
    }
    

    结果测试

    public class Main {
        public static void main(String[] args) {
            ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
            MyLogger logger = new MyLogger();
            ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
            System.out.println(proxy.add(1, 9));
            System.out.println(proxy.mul(3, 3));
    
        }
    }
    /**
    The method add begins with[1,9]
    The method add ends with[3]
    10
    The method add begins with[3,3]
    The method add ends with[3]
    9
    */
    

    总结

    这是一个很基础的静态代理,业务类 ArithmeticCalculatorImpl 只需要关注业务逻辑本身,保证了业务的重用性,这也是代理类的优点,没什么好说的。我们主要说说这样写的缺点:

    • 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
    • 如果接口增加一个方法,比如 ArithmeticCalculatorImpl 增加归零 changeZero()方法,则除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

    4. 解决方法二:使用动态代理

    我们去掉 ProxyLogger. 类,增加一个 ObjectInterceptor.java 类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ObjectInterceptor implements InvocationHandler {
    
        //目标类
        private Object target;
    
        //切面类(这里指 日志类)
        private MyLogger logger;
    
        public ObjectInterceptor(Object target, MyLogger logger) {
            this.target = target;
            this.logger = logger;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //参数日志
            logger.showParam((int)args[0], (int)args[1]);
            System.out.println(args[0]+"--"+args[1]);
            int result = (int)method.invoke(target, args);
            //结果日志
            logger.showResult(result);
            return result;
        }
    }
    

    测试

    public class Main {
        public static void main(String[] args) {
            MyLogger logger = new MyLogger();
            ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
            System.out.println(proxy.add(1, 9));
            System.out.println(proxy.mul(3, 3));*/
    
            Object object = new ArithmeticCalculatorImpl();
            MyLogger logger = new MyLogger();
            ObjectInterceptor objectInterceptor = new ObjectInterceptor(object, logger);
            /**
             * 三个参数的含义:
             * 1、目标类的类加载器
             * 2、目标类所有实现的接口
             * 3、拦截器
            */
            ArithmeticCalculator calculator = (ArithmeticCalculator) Proxy.newProxyInstance(object.getClass().getClassLoader(),
                    object.getClass().getInterfaces(), objectInterceptor);
    
            System.out.println(calculator.add(1, 5));
       		 /*
                The method add begins with[1,5]
                1--5
                The method add ends with[3]
                6
    		 */
        
    

     那么使用动态代理来完成这个需求就很好了,后期在 ArithmeticCalculator 中增加业务方法,都不用更改代码就能自动给我们生成代理对象。而且将 ArithmeticCalculator 换成别的类也是可以的。也就是做到了代理对象能够代理多个目标类,多个目标方法。

     注意:我们这里使用的是 JDK 动态代理,要求是必须要实现接口。与之对应的另外一种动态代理实现模式 Cglib,则不需要,我们这里就不讲解 cglib 的实现方式了。

    不管是哪种方式实现动态代理。本章的主角:AOP 实现原理也是动态代理

  • 相关阅读:
    MYSQL limit用法
    mybaties mapping中if
    执行数据库同时又调接口
    WITH (NOLOCK)
    SpringMVC转发和重定向区别!
    MyBatis的foreach语句详解
    SSM mapper.xml
    win7与virtualbox中centos文件共享
    PBOC2.0中消费交易流程
    PBOC2.0协议中电子存折/电子钱包中圈存交易流程
  • 原文地址:https://www.cnblogs.com/haixiang/p/10202553.html
Copyright © 2020-2023  润新知