关于java中jdk中接口动态代理模式Proxy的剖析

来源:互联网 发布:ipad软件连不上网络 编辑:程序博客网 时间:2024/05/22 15:32

这个示例将展示jdk创建代理对象、调用实际对象、实际对象处理的整个过程,首先jdk动态代理只是针对接口而言的,即根据用户提供的接口创建一个实现接口的代理类并且把用户传入的实际对象作为代理对象的属性,当调用代理对象实现接口方法时就委托给实际对象,并在调用实际对象方法进行拦截让用户实现业务),以下是一个具体实例演示,并且包括jdk创建的不可见的代理类源代码展示。

1、定义接口如下:

package com.test.bean;

public interface IDAO {
 public void add(Object instance);
 public void update(Object instance);
 public void delete(Object instance);

}

2、定义接口实现类如下:

package com.test.bean;

public class BaseDAO implements IDAO {

 public void add(Object instance) {
  // TODO Auto-generated method stub
  System.out.println("add");
 }

 public void delete(Object instance) {
  // TODO Auto-generated method stub
  System.out.println("delete");
 }

 public void update(Object instance) {
  // TODO Auto-generated method stub
  System.out.println("update");
 }

}

3、定义拦截器如下:

package com.test.bean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DAOInvocationHandler implements InvocationHandler {
 private IDAO instance;
 public DAOInvocationHandler(IDAO instance)
 {
  this.instance=instance;
 }

 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  // TODO Auto-generated method stub
  return method.invoke(instance, args);
 }

}

4、定义实现类如下:

public class Test

{

        public static void main(String[] args)

       {

               IDAO dao=getProxy(new BaseDAO());
              dao.add(null);
              dao.update(null);
              dao.delete(null);

        }

         public  IDAO getProxy(IDAO instance) throws MalformedURLException
         {

              IDAO dao=(IDAO)Proxy.newProxyInstance(classLoader, new Class[]{IDAO.class},new DAOInvocationHandler(instance));

         }

}

 

下面进行原理剖析:

 

       IDAO dao=(IDAO)Proxy.newProxyInstance(classLoader, new Class[]{IDAO.class},new DAOInvocationHandler(instance));

       System.out.println(classLoader.loadClass(dao.getClass().getName()));//输出$Proxy0

      上面代码之心的结果是:生成了一个名叫$Proxy0的对象,那它所对应的java文件就是$Proxy0.java,对应的class文件是$Proxy0.class

       这是按照我们的编程的角度来分析的,但是实际上是jdk自动根据用户的接口和用户指定的类装载器并且自动生成了一个二进制的字节码的class文件,最后使用用户的ClassLoader来装载到jvm上运行的。可能有人会怀疑者怎么可能了,我们也看不见,你是不是在瞎说了,其实不然,请看我贴出的它实际代码:

 

import com.test.bean.IDAO;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class Proxy0 extends Proxy
  implements IDAO
{
  private static Method m1;
  private static Method m5;
  private static Method m3;
  private static Method m0;
  private static Method m4;
  private static Method m2;

  public Proxy0()
    throws
  {
    super(paramInvocationHandler);
  }

  public final boolean equals()
    throws
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void update()
    throws
  {
    try
    {
      this.h.invoke(this, m5, new Object[] { paramObject });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void add()
    throws
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramObject });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void delete()
    throws
  {
    try
    {
      this.h.invoke(this, m4, new Object[] { paramObject });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws
  {
    try
    {
      return ((String)this.h.invoke(this, m2, null));
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m5 = Class.forName("com.test.bean.IDAO").getMethod("update", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.test.bean.IDAO").getMethod("add", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m4 = Class.forName("com.test.bean.IDAO").getMethod("delete", new Class[] { Class.forName("java.lang.Object") });
      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());
    }
  }
}

 

上面的代码是通过查看jdk中Proxy的反编译代码然后想了一个方法给还原成class文件然后再反编译成java文件而成的具体代码如下:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
       "$Proxy0", new Class[]{IDAO.class});
  FileOutputStream out=new FileOutputStream(new File("D:$Proxy0.class"));
  out.write(proxyClassFile);
  out.close();

 

那么下面就让我们一起来看一下它是怎样产生这些代码的?

 

分析:1、生成的代理类文件为什么要继承Proxy?
                      答案:从下面的代码中可以看出例如在调用equal方法时调用InvocationHandler的invoke进行拦截(做业务处理)。
                public final boolean equals()
                throws
                {
                     try
                     {
                        //代理方法切入点h是InvocationHandler的实例存在于Proxy中。
                        return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
                      }
                      catch (RuntimeException localRuntimeException)
                      {
                        throw localRuntimeException;
                      }
                      catch (Throwable localThrowable)
                      {
                        throw new UndeclaredThrowableException(localThrowable);
                      }
                 }
                      2、生成的代理类文件为什么要实现用户接口?
                      答案:用于拦截用户对象需要拦截的方法。
                      3、为什么需要InvocationHandler接口的实现类?
                      答案:InvocationHandler接口用户在用户对象的方法调用之前和之后进行的拦截好做业务处理。
                      public class DAOInvocationHandler implements InvocationHandler {
                     private IDAO instance;
                     public DAOInvocationHandler(IDAO instance)
                     {
                    this.instance=instance;
                     }

                     public Object invoke(Object proxy, Method method, Object[] args)
       throws Throwable {
              // TODO Auto-generated method stub
                            //调用之前的业务处理
                            Obejct result=method.invoke(instance, args);
                            //调用之后的业务处理
              return result;
                     }

                       }
                       4、Proxy类中为什么需要ProxyGenerator类?
                       答案:ProxyGenerator用户生成二进制字节码的代理class类文件。
                       5、Proxy的缺点和优点?
                       答案:优点效率高,缺点:只能对实现接口的类进行代理换句话说:要进行代理,类必须实现接口。
                /*
   * Generate the specified proxy class
                 * 根据Proxy类,和用户接口生一个继承Proxy并且实现用户接口的代理类二进制字节码类文件然后装载到jvm中运行。
   */
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
      proxyName, interfaces);
  try {
                    //使用loader类装载器装载临时生成的字节码类文件到jvm中。
      proxyClass = defineClass0(loader, proxyName,
   proxyClassFile, 0, proxyClassFile.length);
  } catch (ClassFormatError e) {
      /*
       * A ClassFormatError here means that (barring bugs in the
       * proxy class generation code) there was some other
       * invalid aspect of the arguments supplied to the proxy
       * class creation (such as virtual machine limitations
       * exceeded).
       */
      throw new IllegalArgumentException(e.toString());
  }

 

这是我的一点点总结希望对大家有所帮助。