Java中的动态代理

这几天打算研究一下Retrofit,遇到的第一个知识点就是动态代理 。动态代理对应设计模式中的代理模式,还有个模式叫做静态代理,我们知道代理类与目标类需要实现相同的接口,在静态代理中手动实现这些接口会产生大量的模板代码,动态代理就可以很好地解决这一问题。

1、示例

接下来的例子,我们使用动态代理为网络请求添加日志,首先需要定义请求接口。

public interface HttpRequest {
    String request(String path);
}  

然后实现真正的网络请求类,这里直接返回字符串。

public class HttpRequestImpl implements HttpRequest {
    @Override
    public String request(String path) {
        return "<html></html>";
    }
}

接着新建一个实现InvocationHandler接口的回调类,每个代理类实例都会与一个回调接口关联,所有对代理类接口中方法的调用都会被转发到该接口的 invoke 方法。这里我们的网络请求接口只有一个方法,如果代理类实现的接口有多个方法,还需要根据 invoke 方法的 method 参数判断当前正在调用哪一个方法,再执行相应的逻辑。

public class HttpRequestProxy implements InvocationHandler {

    private Object mTarget;

    public Object bind(Object obj) {
        mTarget = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println("log ---> path: " + objects[0]);
        String result = (String) method.invoke(mTarget, objects);
        System.out.println("log ---> content: " + result);
        return result;
    }
}  

在使用时,我们只需要将目标对象绑定到回调接口中,然后将Proxy类的 newProxyInstance 方法返回的代理类实例转换为其实现的接口即可。

public class Main {

    public static void main(String[] arg) {
        HttpRequestImpl impl = new HttpRequestImpl();
        HttpRequestProxy proxy = new HttpRequestProxy();
        HttpRequest request = (HttpRequest) proxy.bind(impl);
        request.request("localhost");
    }
    // 输出:
    // log ---> path: localhost
    // log ---> content: <html></html>
}

2、原理

这一部分我们从源码角度分析动态代理的实现机制,先看一下之前示例里系统生成的代理类:

public final class $Proxy0 extends Proxy implements HttpRequest {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            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 String request(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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);
        }
    }

    static {
        try {
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.test.HttpRequest").getMethod("request", Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

生成的代理类先在静态代码块中初始化对应成员方法的Method对象,而所有成员方法都是调用与其实例关联的回调接口的 invoke 方法,并将自身对应的Method对象和参数传递给。除了我们接口中的函数外,系统在还代理类中帮我们复写了 equals toString hashCode 这三个Object类的方法。

接下来以Proxy类的 newProxyInstance 方法为切入点,分析代理类的生成过程。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
    throws IllegalArgumentException {
    Class<?> cl = getProxyClass0(loader, interfaces);
    Constructor<?> cons = cl.getConstructor(constructorParams);
    return cons.newInstance(new Object[]{h});
}  

先是通过 getProxyClass0 方法获取到了代理类的Class对象,然后通过反射将我们的回调接口作为参数调用其构造函数。

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}  

proxyClassCache 是一个WeakCache类型的变量,如果对应的代理类已创建就直接返回,否则其内部会调用ProxyClassFactory中的函数生成代理类。  

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    // 首先判断接口数组中是否与重复元素。
    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    for (Class<?> intf : interfaces) {
        Class<?> interfaceClass = null;
        if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
            throw new IllegalArgumentException(
                "repeated interface: " + interfaceClass.getName());
        }
    }
    // 接下来确定代理类所在的包,如果所有接口都被public修饰,那么代理类的包由系统
    // 决定。如果如果存在非public型的接口,那么代理类需要与其在一个包下。如果有多个
    // 非public型接口并且所在的包不同则抛出异常。
    String proxyPkg = null; 
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    for (Class<?> intf : interfaces) {
        int flags = intf.getModifiers();
        if (!Modifier.isPublic(flags)) {
            accessFlags = Modifier.FINAL;
            String name = intf.getName();
            int n = name.lastIndexOf('.');
            String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
            if (proxyPkg == null) {
                proxyPkg = pkg;
            } else if (!pkg.equals(proxyPkg)) {
                throw new IllegalArgumentException(
                    "non-public interfaces from different packages");
            }
        }
    }
    if (proxyPkg == null) proxyPkg = "";
    {
        // 获取接口中的所有方法,检查这些接口的方法中是否存在方法名与参数列表相同
        // 但返回值类型却不同的方法。在这个方法中系统首先将hascode, toString, equals
        // 这三个函数添加到集合中。
        List<Method> methods = getMethods(interfaces);
        Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
        validateReturnTypes(methods);
        // 如果接口中存在重复的方法,则按接口的排序仅保留第一个方法。
        List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
        Method[] methodsArray = methods.toArray(new Method[methods.size()]);
        Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
        long num = nextUniqueNumber.getAndIncrement();
        // 生成代理类名称,默认为:$Proxy0、$Proxy1、$Proxy2...
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
        return generateProxy(proxyName, interfaces, loader, methodsArray,
                             exceptionsArray);
    }
}   

最后代理类将在 generateProxy 方法中产生,这是一个native函数,它会在内存中拼凑出对应代理类class文件的数组,然后加载到虚拟机中。

posted @ 2018-03-30 10:06  mmmmar  阅读(248)  评论(0编辑  收藏  举报