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动态代理的两个缺点!  在我之后的随笔中会继续讨论!

  • 相关阅读:
    POJ 1659 Frogs' Neighborhood
    zoj 2913 Bus Pass(BFS)
    ZOJ 1008 Gnome Tetravex(DFS)
    POJ 1562 Oil Deposits (DFS)
    zoj 2165 Red and Black (DFs)poj 1979
    hdu 3954 Level up
    sgu 249 Matrix
    hdu 4417 Super Mario
    SPOJ (BNUOJ) LCM Sum
    hdu 2665 Kth number 划分树
  • 原文地址:https://www.cnblogs.com/lxy-java/p/12821534.html
Copyright © 2011-2022 走看看