JAVA讲到代理模式就绕不开newProxyInstance方法,今天简单说一下。
Proxy静态方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
一共有三个参数:
- loader:用于指定用哪个类加载器,去加载生成的代理类;
- interfaces:指定这个代理类能够代理目标对象的哪些方法和接口;
- h:重要!!用来指定生成的代理对象在方法被调用时如何进行处理;
我们走读下里面重要的逻辑:
getProxyClass0
- 生成一个代理class
- 如果
proxy class
之前有缓存,就不重复创建,不然走ProxyClassFactory
创建
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);
}
ProxyClassFactory
ProxyClassFactory 是 Proxy 里的一个内部类,用来生成代理类,apply() 方法最后调用ProxyGenerator.generateProxyClass() 方法来完成生成字节码的操作,这里不讲它是如何生成的,我们只要知道它可以返回一个全新的 class 类,如果想看这个 class 类的内容,可以在测试类中设置一个Propertie:
public class Test {
public static void main(String[] args) {
//加入这一段可以在磁盘中生成 代理类,让我们看到代理类的真面目 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Sourceable source = new Source();
Sourceable test = (Sourceable) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{Sourceable.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("test");
if ("method".equals(method.getName())) {
return method.invoke(source, null);
}
return null;
}
});
test.method();
}
InvocationHandler
看生成的代理类之前先简单看看 InvocationHandler 唯一的方法注释。
public class InvocationHandler {
/**
* @param proxy:代理实例,即当前代理对象本身。当 invoke 方法被调用时,这个参数就是代理对象的一个引用。
* @param method:被调用的方法对象。它表示代理实例上被调用的方法,在 invoke 方法中可以需要调用 method.invoke 以触发方法。
* @param args:调用方法时传递的参数数组。若没有参数,则该数组为 null。
*/
public Object invoke(Object proxy, Method method, Object[] args);
}
代理类 $Proxy0.class
最后生成的代理类总结:
- $Proxy0 实现了我们给的接口 Sourceable 并且继承了 Proxy ,构造类要传入我们使用newProxyInstance 时用的 InvocationHandler 变量。
- static静态初始化代码块,把 Sourceable 接口的各个方法 Method 初始化完成(包括equals,toString 等方法)。
- **真正执行方法的时候实际上是执行 InvocationHandler 对象的 invoke() 方法( super.h 就是保存在父类 Proxy 中的 InvocationHandler 对象)。**各个方法的区别就是 Method 对象不同而已,所以真正方法的执行是在 InvocationHandler 的 invoke() 方法中完成的。 这也说明了代理逻辑 和 动态代理本身是代码分离的,程序员只需要关注好自己的代理逻辑就行,动态代理本身就交给 JDK 本身去处理。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import cn.xxywithpq.proxy.jdkproxy.bean.Sourceable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Sourceable {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
private static Method m4;
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 void method() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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);
}
}
public final void method1(int var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("cn.xxywithpq.proxy.jdkproxy.bean.Sourceable").getMethod("method");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m4 = Class.forName("cn.xxywithpq.proxy.jdkproxy.bean.Sourceable").getMethod("method1", Integer.TYPE);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
总结
在 JDK 动态代理中,美中不足就是整个设计都是针对接口做的代理,如果是普通的类,我们无法通过这个方式代理对象(通过生成的代理类也知道没有接口是不行的),但是我们知道 通过拼接字节码生成新的类 自由度是十分大的,这也就启示我们 设计不管是针对接口类还是普通类的代理类 是完全可行的,比如cglib框架就是通过拼接字节码来实现非接口类的代理。