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

浙公网安备 33010602011771号