zoukankan      html  css  js  c++  java
  • Spring AOP底层原理之动态代理模式

    什么是AOP?

      AOP(Aspect Oriented Programming)  面向切面编程。

      指在程序运行期间,将某段代码动态切入到指定位置进行运行的这种编程方式。

    什么是动态代理?

      有一个这样的场景  在执行业务逻辑代码的时候加上日志输出  我们可以尝试用动态代理的方法实现

      首先定义一个业务逻辑的接口(很重要!下面解释), 里面有业务逻辑的一些方法

      

    public interface MyTask{
          void method1(Object arg1,Object arg2);
          void method2(Object arg1);    
    }    

      真实的业务实现了这个接口

    public class Task1 implements MyTask{
    
        public void method1(Object arg1, Object arg2) {
            System.out.println("执行业务1...");
        }
    
        public void method2(Object arg1) {
            System.out.println("执行业务2...");
        }
    }

      现在我们可以添加日志功能,如果不使用动态代理   我们可以在每个业务方法内部加入日志输入  如下

    public class Task1 implements MyTask{
    
        public void method1(Object arg1, Object arg2) {
            System.out.println("日志:method1方法前……");
            System.out.println("执行业务1...");
            System.out.println("日志:method1方法后……");
        }
    
        public void method2(Object arg1) {
            System.out.println("日志:method2方法前……");
            System.out.println("执行业务2...");
            System.out.println("日志:method2方法后……");
        }
    }

      但是这样做有明显的缺点:

        1)如果有大量的方法  日志写起来非常的麻烦  

        2)日志是辅助功能,业务是核心功能  这样辅助和核心就写到了一起  存在耦合问题!

      我们可以用动态代理模式解决上述问题

        首先我们的Task2  中的业务逻辑  不加任何的日志输出

        

    public class Task2 implements MyTask{
        public void method1(Object arg1, Object arg2) {
            System.out.println("执行业务1...");
        }
    
        public void method2(Object arg1) {
            System.out.println("执行业务2...");
        }
    }

        我们需要创建一个代理类ProxyHandler 实现 InvocationHandler 方法如下   

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ProxyHandler implements InvocationHandler {
        /**
         *  传入真实角色
         */
        private Object target;
        /**
         *
         * @param target 注入真实角色
         */
        public void setTarget(Object target) {
            this.target = target;
        }
        /**
         * @param proxy  代理对象;给jdk使用,任何时候都不要动这个对象
         * @param method  当前将要执行目标对象的方法
         * @param args  这个方法调用时候  外界传入的值
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("日志:执行" + method.getName() + "前");
            method.invoke(target,args);
            System.out.println("日志:执行" + method.getName() + "后");
            return null;
        }
    }

      最后可以测试执行

    import java.lang.reflect.Proxy;
    public class Client {
        public static void main(String[] args) {
            Task2 task2 = new Task2();
            ProxyHandler handler = new ProxyHandler();
            handler.setTarget(task2);
    //        设置要代理类的对象
            /*
                通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
                第一个参数:task2.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
                          就是task2的类加载器
    
                第二个参数:task2.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,
                          这样代理对象就能像真实对象一样调用接口中的所有方法
    
                第三个参数:handler,我们将代理对象关联到上面的ProxyHandler自定义对象上
                          方法执行器,帮我们目标对象执行目标方法
             */
            MyTask proxy = (MyTask) Proxy.newProxyInstance(task2.getClass().getClassLoader(),
                    task2.getClass().getInterfaces(),handler); // 动态生成代理类
            proxy.method1(1,2);
            proxy.method2(1);
        }
    }

      执行结果如下 

      日志:执行method1前
      执行业务1...
      日志:执行method1后
      日志:执行method2前
      执行业务2...
      日志:执行method2后

    动态代理类的缺点

      1、复杂难写

      2、java默认的动态代理  必须实现接口  否则不能动态代理

        原因:首先我们看一下动态代理Proxy的类  它是哪个类?

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(proxy.getClass());
            return null;
        }
    
    执行结果如下:class com.sun.proxy.$Proxy0

      这说明 proxy的类和我们的Task2根本不是一个类  那么proxy为什么能执行我们定义的Task2类中的方法呢?

      这是因为proxy和我们的Task2实现了同一个接口  那就是MyTask  这也就是为什么我们能放心的将proxy类强制转换为MyTask的原因

      所以java jdk的动态代理必须实现接口

    Spring 中AOP底层也是使用的动态代理  但是它解决了java动态代理的两个缺点!  在我之后的随笔中会继续讨论!

  • 相关阅读:
    给出一个单向链表的头指针,输出该链表中倒数第K个节点的指针
    把二元查找树转变成排序的双向链表
    动态规划0/1背包问题
    判断一字符串是不是对称的
    动态链接库dll,静态链接库lib, 导入库lib(转载)
    基于格点数据分析和表达
    Silverlight Wcf 获取集合数据并绑定(DataContract,DataMember)
    让你学会使用WCF服务端配置
    数据契约(DataContract)
    symbol详解
  • 原文地址:https://www.cnblogs.com/lxy-java/p/12821534.html
Copyright © 2011-2022 走看看