zoukankan      html  css  js  c++  java
  • 代理模式及jdk动态代理原理

    代理模式 :为其它对象提供代理,以控制对这个对象的访问。

    代理模式的特征:代理类(proxyClass)与委托类(realClass)有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类(调用realClass的方法,实现代理的功能),以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 

    作用:主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等

     动态代理结论:Proxy.newProxyInstance根据classloader和接口数组,生成一个$Proxy0代理类,并将InvocationHandler的实现类做为构造函数参数传递给$Proxy0代理类,代理类$Proxy0调用接口的方法,会在方法中调用InvocationHandler的实现类中的invoke()方法。invoke()方法中,通过反射调用委托类(realClass)的实际方法,完成了整个代理操作。

    1.为了更好的理解代理模式,先理解静态代理

    模拟支付宝支付,大家在淘宝买过东西都知道,买家付款后,钱放在支付宝里,不会立刻给商家,待用户确认收货后,才会支付给商家,这里支付宝就相当于代理用户付款给商家。

    支付接口

    package com.proxy;
    
    public interface Pay {
        public boolean pay();
    }

    委托类实现了支付接口

    package com.proxy;
    
    public class Customer implements Pay{
        @Override
        public boolean pay() {
            System.out.println("网上购物使用支付宝结账付款");
            return true;
        }
    }

    代理类也要实现pay接口,包含委托类对象(关联关系)

    package com.proxy;
    
    public class AliPay implements Pay{
        private Customer customer = null;
        public AliPay(Customer customer){
            this.customer = customer;
        }
        
        @Override
        public boolean pay() {
            if(customer.pay()){
                System.out.println("用户收到货物,支付包支付给商家完成");
            }
            return customer.pay();
        }
    }

    测试

    package com.proxy;
    
    public class TestProxy {
        public static void main(String[] args) {
            Customer customer = new Customer();
            AliPay aliPay = new AliPay(customer);
            if(aliPay.pay()){
                System.out.println("整个购物流程完成!");
            }else{
                System.out.println("付款失败!");
            }
        }
    }

    结果:

    网上购物使用支付宝结账付款
    用户收到货物,支付包支付给商家完成
    整个购物流程完成!

    使用静态代理发现,代理类只能为一个接口服务,这样会产生许多代理类。这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 

    动态代理:

    代理类

    package com.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyPay implements InvocationHandler {
        private Object target;  
        
        /**
         * @param proxy  代理的实例proxy instance  
         * @param method 代理的实例proxy instance调用接口的方法
         * @param args   调用实际类方法的参数数组,没有参数为null,基本类型会转成封装类
         * @return
         * @date 2016-4-7
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            Object pay =  method.invoke(target, args);
            if((Boolean) pay){
                System.out.println("用户收到货物,支付包支付给商家完成");
            }
            return pay;
        }
    
        
        /**
         * loader : 类加载器
         * interfaces : 代理类实现的接口
         * h :调用方法
         * 
         * public static Object newProxyInstance(ClassLoader loader,
                          Class<?>[] interfaces,
                          InvocationHandler h)
         */
        public Object bind(Object target) {  
            this.target = target;  
            //取得代理对象  
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                    target.getClass().getInterfaces(), this);   
        }  
    
    }

    动态代理测试

    package com.proxy;
    
    public class TestDynamicProxy {
        public static void main(String[] args) {
            Customer customer = new Customer();
            ProxyPay proxyPay = new ProxyPay();
            Pay pay =  (Pay) proxyPay.bind(customer);
            if(pay.pay()){
                System.out.println("整个购物流程完成!");
            }else{
                System.out.println("付款失败!");
            }
        }
    }

    结果:

    网上购物使用支付宝结账付款
    用户收到货物,支付包支付给商家完成
    整个购物流程完成!

    虽然完成了动态代理的代码操作,但是对于整个流程还是有些疑惑,是怎么调用代理类中的invoke方法。

    于是看源代码发现

    public static Object newProxyInstance(ClassLoader loader,
                          Class<?>[] interfaces,
                          InvocationHandler h)
        throws IllegalArgumentException
        {
        if (h == null) {
            throw new NullPointerException();
        }
    
        /*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass(loader, interfaces);
    
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            Constructor cons = cl.getConstructor(constructorParams);
            return (Object) cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
        }
        }
    /*
    * Returns the <code>java.lang.Class</code> object for a proxy class
    * given a class loader and an array of interfaces. The proxy class
    * will be defined by the specified class loader and will implement
    * all of the supplied interfaces. If a proxy class for the same
    * permutation of interfaces has already been defined by the class
    * loader, then the existing proxy class will be returned; otherwise,
    * a proxy class for those interfaces will be generated dynamically
    * and defined by the class loader.
    */
    getProxyClass方法就是根据给定的classload和interface生成代理类,如果存在,就返回存在的代理类。
    cons.newInstance(new Object[] { h })可以看出代理类中的构造函数是传进去的InvocationHandler对象,会调用InvocationHandler实现类的invoke方法


    package com.proxy;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import sun.misc.ProxyGenerator;
    public class ProxyPay implements InvocationHandler {
        private Object target;  
        private static boolean flag = true;
        /**
         * @param proxy  代理的实例proxy instance  
         * @param method 代理的实例proxy instance调用接口的方法
         * @param args   调用实际类方法的参数数组,没有参数为null,基本类型会转成封装类
         * @return
         * @date 2016-4-7
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            /**
             * Generate a proxy class given a name and a list of proxy interfaces.
             */
            String name = "$Proxy0.class";
            byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Pay.class});
            FileOutputStream os = new FileOutputStream(new File(ProxyPay.class.getClassLoader().getResource("").getPath(),name));
            os.write(bytes);
            
            System.out.println(proxy.getClass().getName());
            Object pay =  method.invoke(target, args);
            if((Boolean) pay){
                System.out.println("用户收到货物,支付包支付给商家完成");
            }
            if(flag){
                flag = false;
                System.out.println("---------------");
                System.out.println(((Pay) proxy.getClass().getConstructor(InvocationHandler.class).newInstance(this)).pay());
            }
            return pay;
        }
    
        
        /**
         * loader : 类加载器
         * interfaces : 代理类实现的接口
         * h :调用方法
         * 
         * public static Object newProxyInstance(ClassLoader loader,
                          Class<?>[] interfaces,
                          InvocationHandler h)
         */
        public Object bind(Object target) {  
            this.target = target;  
            //取得代理对象  
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                    target.getClass().getInterfaces(), this);   
        }  
    
    }
    
    

    结果:

    $Proxy0
    网上购物使用支付宝结账付款
    用户收到货物,支付包支付给商家完成
    ---------------
    $Proxy0
    网上购物使用支付宝结账付款
    用户收到货物,支付包支付给商家完成
    true
    整个购物流程完成!

    通过proxy.getClass().getName()可以看到proxy代理类是$Proxy0,在内存中存储,通过反射得到此类的构造函数传入了InvocationHandler,调用接口的pay()方法,
    发现会调用InvocationHandler实现类的invoke方法,这也证明了上面说的是正确的。使用ProxyGenerator.generateProxyClass(name, new Class[]{Pay.class});生成了$Proxy0的源码,
    通过下面的源码pay()方法的return ((Boolean)this.h.invoke(this, m3, null)).booleanValue();可以看到会调用InvocationHandler实现类的invoke方法。
    package $Proxy0;
    
    import com.proxy.Pay;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class class extends Proxy
      implements Pay
    {
      private static Method m1;
      private static Method m3;
      private static Method m0;
      private static Method m2;
    
      public class(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject)
        throws 
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final boolean pay()
        throws 
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m3, null)).booleanValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final int hashCode()
        throws 
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String toString()
        throws 
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("com.proxy.Pay").getMethod("pay", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    }


    结论:Proxy.newProxyInstance根据classloader和接口数组,生成一个$Proxy0代理类,并将InvocationHandler的实现类做为构造函数参数传递给$Proxy0代理类,代理类$Proxy0调用接口的方法,会在方法中调用InvocationHandler的实现类中的invoke()方法。invoke()方法中,通过反射调用委托类(realClass)的实际方法,完成了整个代理操作。



  • 相关阅读:
    前后端不分离的springboot项目问题:页面框架问题
    SpringBoot使用Filter过滤器处理是否登录的过滤时,用response.sendRedirect()转发报错
    mysql千万级数据优化查询
    java进阶学习的一些思路
    java的List列表转成Tree(树形)结构列表
    cmd 一键获取 所有连接过的wifi 密码
    SQLMap用户手册【超详细】
    Python:SQLMAP参数中文解释
    初识sql注入及sqlmap
    wwwscan网站目录文件批量扫描工具
  • 原文地址:https://www.cnblogs.com/chenxiaocai/p/5365134.html
Copyright © 2011-2022 走看看