Java动态代理

来源:互联网 发布:北京值得去的地方知乎 编辑:程序博客网 时间:2024/06/18 11:02
  • 代理模式

  • Java动态代理

  • 动态代理实现


代理是一种设计模式。在一些情况下,一个对象并不能或者并不希望被其他某些调用,代理就可以在这其中起到中介的作用。
这样既可以使用真实对象的方法,又可以保护目标对象,还可以在真实方法调用的前后进行一些操作。

常见的代理模式实现方式如下:
定义一个接口:

public interface Subject {    String doSth(String name);    }

实现接口:

    public class RealSubject implements Subject{    public String doSth(String name) {        return String.format("I'm %s, i am doing Real thing now...",new String[]{name});    }    }

如果有这样的情况,我们并不想让测试类直接调用RealSubject的doSth方法
实现接口,创建一个代理的类,让代理类来调用RealSubject对象的方法 :

    public class ProxySubject implements Subject{    private  Subject realSubject;    public  ProxySubject(Subject realSubject)  {this.realSubject = realSubject ;}    public String doSth(String name)  {        System.out.println("Proxy start -----");        System.out.println(realSubject.doSth(name));  // 此处执行真实对象的request方法        System.out.println("Proxy end -----");        return "OK";    }    }

测试:

    public class Test {    public static void main(String[] args) {        RealSubject realSubject = new RealSubject();        Subject subject = new ProxySubject(realSubject);        subject.doSth("Irving");    }    }

返回的结果:
Proxy start —–
I’m Irving, i am doing Real thing now…
Proxy end —–

上面实现了一个简单的代理模式,被代理对象的类(RealSubject)和代理类(ProxySubject)都需要实现同一个接口,这会导致
1. 增加维护的复杂度;将来修改接口的时候要改动两个实现,
2. 增加代码量:而且ProxySubject在这里只做了一个“壳”的作用,特意给它写个类实在有点不划算。

此时的代理方式,在程序运行之前就已经确定了谁代理谁。然后按照固定的方式将被代理的对象传入代理类,然后再调用代理类中的方法。

这是静态代理方式。

JDK提供了一种“动态代理”方式:
动态代理类的源码是在程序运行期间由JVM根据反射等动态机制生成的,所以不存在代理类的字节码文件,代理类和被代理的类是在运行期间确定的代理关系。

接下来看看如何使用动态代理的方式生成上面的接口实现RealSubject的代理。

先实现一个处理器,实现InvocationHandler,invoke方法定义了如何处理方法调用时的逻辑,简单处理下:

    public class DynamicProxy implements InvocationHandler{    private Object object;    public DynamicProxy(Object obj){        object = obj;    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("Dynamic Proxy start -----");        System.out.println(method.invoke(object, args));        System.out.println("Dynamic Proxy end -----");        return "OK";    }    }

调用类实现:

    public class TestDynamic {    public static void main(String[] args) {        Subject realSubject = new RealSubject();        InvocationHandler invocationHandler = new DynamicProxy(realSubject);        Class cls = realSubject.getClass();        Subject subject = (Subject)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),invocationHandler);        subject.doSth("IRVING");    }    }

从DEBUG说起,可以DEBUG看看生成的Subject subject是什么类型的:
这里写图片描述

$Proxy0类型的,这明显不是我们自定义的类型。

内部类的定义有规范是 A $ B 的格式,但是这里是以$开头的。感觉应该是标记说明是运行时生成的类对象。观察发现这个对象可以转化成Subject对象,说明这个类可能是实现了这个接口(也可能继承了这个接口的实现类)。

这个对象是如何生成的?
Proxy.newProxyInstance方法传入了三个参数:
加载被代理类的ClassLoader、被代理类型的接口列表、自定义的InvocationHandler实现。
该方法中,有这样一句:

    Class<?> cl = getProxyClass0(loader, interfaces);

核心的,这个方法有如下代码:

     String proxyName = proxyPkg + proxyClassNamePrefix + num;                /*                 * Verify that the class loader hasn't already                 * defined a class with the chosen name.                 */                /*                 * Generate the specified proxy class.                 */                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                    proxyName, interfaces);                try {                    proxyClass = defineClass0(loader, proxyName,                        proxyClassFile, 0, proxyClassFile.length);                } catch (ClassFormatError e) {

其中的proxyClassNamePrefix 就是:

        /** prefix for all proxy class names */    private final static String proxyClassNamePrefix = "$Proxy";

所以我们的代理类命名为:$Proxy加上一个数字。

    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                proxyName, interfaces);

这句生成了代理类的字节数组,并且结合后面一句,从内存中加载出来创建了代理类。

再简要看看ProxyGenerator的generateProxyClass方法:

        public static byte[] generateProxyClass(final String var0, Class[] var1) {        ProxyGenerator var2 = new ProxyGenerator(var0, var1);        final byte[] var3 = var2.generateClassFile();        if(saveGeneratedFiles) {            AccessController.doPrivileged(new PrivilegedAction() {                public Void run() {                    try {                        FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");                        var1.write(var3);                        var1.close();                        return null;                    } catch (IOException var2) {                        throw new InternalError("I/O exception saving generated file: " + var2);                    }                }            });        }        return var3;    }

除了返回class的字节数组,甚至还发现,如果saveGeneratedFiles参数为true的时候,还能把该class保存到磁盘。
稍微修改下main方法:

    public class TestDynamic {    public static void main(String[] args) {        Subject realSubject = new RealSubject();        InvocationHandler invocationHandler = new DynamicProxy(realSubject);        Class cls = realSubject.getClass();        byte[] data = ProxyGenerator.generateProxyClass("$myProxy0", cls.getInterfaces());        FileOutputStream out = null;        try {            out = new FileOutputStream("E:\\$myProxy0.class");            out.write(data);            out.flush();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                out.close();            } catch (IOException ex) {                ex.printStackTrace();            }        }    }    }

用反编译工具查看class的内容:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $myProxy0 extends Proxy  implements Subject{  private static Method m1;  private static Method m0;  private static Method m3;  private static Method m2;  public $myProxy0(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 (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 String doSth(String paramString)    throws   {    try    {      return (String)this.h.invoke(this, m3, new Object[] { paramString });    }    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") });      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);      m3 = Class.forName("Subject").getMethod("doSth", new Class[] { Class.forName("java.lang.String") });      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());  }}

$myProxy0类继承自Proxy并且实现了Subject接口。
除了重写了hashCode和equals方法之外,它也有一个doSth方法,这和我们之前静态代理模式的代理是一个方式,并且doSth方法触发了InvocationHandler对象h的invoke方法。也就是我们自己实现的Handler。
即,调用代理的doSth方法,也就是调用了InvocationHandler的invoke方法。

return (String)this.h.invoke(this, m3, new Object[] { paramString });
0 0