程序员首页投稿(暂停使用,暂停投稿)Android技术知识

戏说代理模式和Java动态代理

2015-12-23  本文已影响3577人  楚云之南

代理

所谓代理,是为某一个实例的访问进行代理和控制。最好的例子是艺人和经纪人的关系,比如沟通档期是一个接口,那么艺人通常会通过经纪人去和第三方沟通档期相关的事情,这样的好处是,沟通档期的经纪人就会限制第三方对艺人的其它事情的打扰,只有沟通档期的请求才会被转发到艺人,其它事务都会通过 沟通档期接口进行限制。

对于演艺公司来说,静态代理就是在一开始为每一个艺人安排一个经纪人,这个经纪人专门负责一个艺人的相关事情。后来公司的管理层发现只有少数大明星的经纪人的事情比较复杂,但是对很多二线 三线的艺人来说,经纪人的事情可能基本上都差不多,但是公司却为每个人都指定了经纪人,导致成本较高(在编程里面表现为很多模板代码)。为了降低成本,公司改变了为每一个艺人都安排一个经纪人的方式,而是在有第三方来沟通艺人档期时,动态安排一个经纪人来为艺人服务,这就是动态代理。

静态代理比较简单,这里不展开描述,而JDK是从1.3版本开始支持动态代理,下面一起看看它的源码。如果你还不明白什么是代理 什么是代理类,建议先google一下。

Java动态代理源码分析

动态代理例子

首先我们还是通过一个简单的例子来说明动态代理是怎么一回事。

public class DynamicProxy {
public static void main(String[] args) {
    Object proxyInstance = Proxy.newProxyInstance(MyInterface.class.getClassLoader(),
            new Class[]{
                    MyInterface.class
            },
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                    if(method.getName().equals("sum")) {
                        return (int)args[0]+(int)args[1];
                    }else if(method.getName().equals("returnMySelf")){
                        return proxy;
                    }
                    return null;
                }
            }
    );

    MyInterface myInterface = (MyInterface) proxyInstance;
    System.out.println(myInterface.sum(1,2));
}

public static interface MyInterface {
    int sum(int arg1,int arg2);
    MyInterface returnMySelf();
}
}

可以看到,我们通过newProxyInstance就产生了一个MyInterface的实例,然后就可以通过myInterface.sum(1,2)来调用sum()方法,然后处理的结果就是我们在InvocationHandler中处理的逻辑,是不是很神奇?下面我们一步步来分析源码是怎么做到的。

newProxyInstance方法

Proxy是实现动态代理的主要类,它提供来产生代理类和代理类实例的方法:newProxyInstance (ClassLoader loader, Class[] interfaces, InvocationHandler h),看看代码

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    //权限判断等,忽略
    //生成代理类
    Class<?> cl = getProxyClass0(loader, intfs);
    //调用构造器,生成实例
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        //各种异常处理
    }
}

getProxyClass0(loader, intfs)中看看:

 private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
     //一个代理类最多能够实现65535个接口
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // 这里Proxy做了一次缓存,如果之前生成过这个Classloader和interfaces的代理类,那么这里直接返回

  //否则新生成类的字节码文件
   byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);
            try {
                    //将字节码加载到JVM
                    proxyClass = defineClass0(loader, proxyName,
                    proxyClassFile, 0, proxyClassFile.length);
              }
        retrun proxyClass;
}

至此,我们生成了一个代理类,然后只需要将它实例化就可以了,可以看到代码中我们把参数InvocationHandler传入了构造器中,这说明我们生成的代理类必然包含了一个接受InvocationHandler的构造器,稍后我们会来分析这个生成的类。

newProxyInstance这个方法实际上做了两件事:第一,创建了一个新的类【代理类】,这个类实现了Class[] interfaces中的所有接口,并通过你指定的ClassLoader将生成的类的字节码加载到JVM中,创建Class对象;第二,以你传入的InvocationHandler作为参数创建一个代理类的实例并返回。

代理类的字节码

上文已经说明了,代理类是通过ProxyGenerator.generateProxyClass(proxyName, interfaces);生成的,这个方法会根据指定的包名和接口,生成一个代理类,我们来测试一下吧。

public class GenerateClass {

static interface Inter {
    int sum(int arg1,int arg2);
}

public static void main(String[] args) throws IOException {
    String path = File.separator+"Users"+File.separator+"pengliang"+File.separator+"InterImplProxy.class";
    //  String path = "c:"+File.separator+"ProxyImpl.class";
    //很简单,生成一个实现了Inter接口的代理类的字节码
    byte[] classCode = ProxyGenerator.generateProxyClass("com.chuyun.InterImplProxy.",new Class[] {Inter.class});
    //字节码写入本地
    FileOutputStream fileOutputStream = new FileOutputStream(path);
    fileOutputStream.write(classCode);
    fileOutputStream.close();
}
}

我们反编译一下这个InterImplProxy.class文件(常见的反编译工具有:JD-GUI),我直接使用IDEA的插件来打开这个class文件,看到如下代码:

  package com.chuyun.InterImplProxy;

  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;
  import java.lang.reflect.UndeclaredThrowableException;
  import proxy.GenerateClass.Inter;

  //实现了Inter 并继承自Proxy ,Proxy有一个接受InvocationHandler的构造器
  public final class InterImplProxy extends Proxy implements Inter {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    static {
    try {
        //反射获取这些函数描述
        m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
        m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        m3 = Class.forName("proxy.GenerateClass$Inter").getMethod("sum", new Class[]{Integer.TYPE, Integer.TYPE});
        m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
  }

  public InterImplProxy(InvocationHandler var1) throws  {
    super(var1);
  }

public final boolean equals(Object var1) throws  {
    try {
        //将所有方法的调用转发给InvocationHandler的invoke方法
        return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}

public final String toString() throws  {
    try {
        return (String)super.h.invoke(this, m2, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

public final int sum(int var1, int var2) throws  {
    try {
        return ((Integer)super.h.invoke(this, m3, new Object[]{Integer.valueOf(var1), Integer.valueOf(var2)})).intValue();
    } catch (RuntimeException | Error var4) {
        throw var4;
    } catch (Throwable var5) {
        throw new UndeclaredThrowableException(var5);
    }
}

public final int hashCode() throws  {
    try {
        return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

}

ok,至此,是不是感觉动态代理也是一个很简单的东西?唯一的区别是我们静态代理中的代理类是由程序猿一开始定义的,而动态代理则是再运行时,通过InvocationHandler来调用我们发送的调用。

总结

  1. 动态代理不支持代理抽象类 ,原因嘛,你看看上面生成的代码,代理类默认已经extends ProxyJava总不允许多重继承;
  2. 动态代理生成的类如果实现的接口都是public,那么这个代理类就处于默认包中(最顶层包,没有包名);如果实现了包权限的接口,那么包名就是这个包权限所在的包名。
  3. 类名字格式为:$ProxyN其中N为Proxy的计数器。之前我们说了,如果某一次动态代理的ClassLoader和 接口个数和顺序完全一样,则Proxy不会生成新的代理类。
  4. InvocationHandlerinvoke方法中的第一个参数就是这个代理类的实例,主要的用途呢就是为了解决有的接口返回的对象就是本身,见例子一中的MyInterface returnMySelf();;但是这里有个需要注意的坑:
    **
    永远不要在InvocationHandlerinvoke里面调用第一个参数proxy的任何方法,因为这样会导致invoke循环无数次调用,最终stackoverflow
    **
上一篇下一篇

猜你喜欢

热点阅读