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文件的数组,然后加载到虚拟机中。