昨晚又温习了一遍JDK动态代理的相关过程,发现看过的印象都已经模糊了,只剩下一点大体的印象。
这种感觉不太好,所以准备记录下这遍回看的历程。
Proxy类:
public class MyProxy {
public static Object proxy(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new MyInvationHandler(target));
}
private static class MyInvationHandler implements InvocationHandler {
private Object target;
public MyInvationHandler(Object target) {
super();
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method:"+method.getName()+" isInvoked!");
// 调用被代理对象的方法
Object res = method.invoke(target, args);
return res;
}
}
}
这里,我们将自己实现的InvocationHandler传入了Proxy.newProxyInstance这个方法中,那么问题来了:我们所写的代理类中并没有显示的将Method方法和属性传入我们自己实现的InvocationHandler中,那么我们怎么知道调用的时候该用哪个方法,又怎么来识别我们传入的参数呢?
别急,我们慢慢看。
既然InvocationHandler中可以做的文章并不多,那么我们把目光稍微上移一些,来看看Proxy.newProxyInstance()这个方法,点进去:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
}
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
}
}
我们可以看到巴拉巴拉一堆,先把异常处理精简一下;
然后从上往下看:
先将传入的接口Class clone一份并将拷贝品设为final类型的,大概是不想在代理时接口数量发生变化,令生成的文件出现一些诡异的情况;SecurityManager看名字就像是做安全管理的,看方法名猜测功能无外乎是:检验是否获取得到代理、是否允许被代理。似乎…和我们的关注点,没什么联系,那就不去分析它了。
接下来的一条
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
这个就比较重要了,我们从下往上看,返回值依赖构造器cons:
return cons.newInstance(new Object[]{h});
构造器cons,就是由这个Class类生成的了。根源啊,想来是很重要的了。
final Constructor<?> cons = cl.getConstructor(constructorParams);
那么这个getProxyClass0()方法究竟做了什么呢?注释说:有就去寻找,没有,创造条件也要去寻找目标代理类,但我深深的知道:Code never lies,comments sometimes do.
所以,点进去看一哈:
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 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);
}
emmmm…注释果然没骗人,我们可以看到:
再进行了短暂的判断后(就判断传来的CLass数组是不是太长了,太长了它表示承受不来),就返回了。看名字proxyClassCache。
Cache-get,一下子就想到了K-V的经典:Map。
点进去一看,但,并不是,而是一个叫WeakCache的东西:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
点进底层实现去看了,但发现会追的很深,而且有些复杂,深追会花很多时间,所以就先不拓展的太细了。其实开始想的也没啥大错,它的底层有一个嵌套的ConcurrentMap,然后经过一长串校验判断以后调用了一个叫subKeyFactory的apply方法创造value。
估计大体作用就就是将生成的class文件缓存起来,并在每一次调用get(K,P)方法时,将过期的Entries清理掉,防止内存泄漏。
所以我们来看下这两个形参的实现,这两个形参都实现了apply()方法:
/**
* A function that maps an array of interfaces to an optimal key where
* Class objects representing interfaces are weakly referenced.
*/
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
这个实现是为了将一个由接口形成的组和一个可选的key映射起来,表示接口的类对象
为弱引用对象,似乎只是做了个map,也没别的了。看另外一个:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
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) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
这个就很关键了:
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
由所给的类加载器和接口数组,创建、定义、返回一个代理类。
首先先创建了字节流数组:
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
而后:
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
这个defineClass0是一个本地方法,看不到实现。但可以从apply()方法的返回类型猜测一二,它应该是返回了一个由代理类动态生成的类。
如果不经过特意将其输出到磁盘的操作,在电脑上磁盘上是找不到这个文件的。推测这个文件应该是在生成了以后直接加载到了内存中缓存了起来,而不会落盘。
这里我将代理生成的类输出到了我的本地磁盘,并对其进行了反编译,结果如下:
package com.sun.proxy;
import Proxy.*;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy implements Girl
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public $Proxy0(final InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final boolean equals(final Object o) {
try {
return (boolean)super.h.invoke(this, $Proxy0.m1, new Object[] { o });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, $Proxy0.m2, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final boolean dating(final float n) {
try {
return (boolean)super.h.invoke(this, $Proxy0.m3, new Object[] { n });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void happy() {
try {
super.h.invoke(this, $Proxy0.m4, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final int hashCode() {
try {
return (int)super.h.invoke(this, $Proxy0.m0, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
static {
try {
$Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
$Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
$Proxy0.m3 = Class.forName("Proxy.Girl").getMethod("dating", Float.TYPE);
$Proxy0.m4 = Class.forName("Proxy.Girl").getMethod("happy", (Class<?>[])new Class[0]);
$Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
}
catch (NoSuchMethodException ex) {
throw new NoSuchMethodError(ex.getMessage());
}
catch (ClassNotFoundException ex2) {
throw new NoClassDefFoundError(ex2.getMessage());
}
}
}
代码也比较长,但好在逻辑并不复杂,在最下面这快的static{}块中,对传入的Method进行了初始化,我这里定义了一个Girl的接口,里面有两个方法。但看得出来初始化的不止两个方法,但我们不去不管它。
在往上面看:生成了方法的实现,所有的方法实现都通过super.h来调用invoke方法。
这个h是从父类中继承的,那么父类中的h又是什么呢?
其实从invoke这个方法就大概可以猜到,这个h就是我们传入的自己实现的InvocationHandler。
不信就再往上看看:
public $Proxy0(final InvocationHandler invocationHandler) {
super(invocationHandler);
}
这样就很清晰了。
上面多出来的三个Object方法,代表这三个方法:equals(),hashcode(),toString() 均会被InvocationalHandler增强,其实也不奇怪,毕竟在Object类中,常常被复写的方法似乎就是这三兄弟。
其他Object方法如果被调用的话,仍然走的是Object本身的逻辑,不会被增强。
梳理到这,一条模糊的线大概就生成了,至于怎么生成的二进制文件,
ProxyGenerator.generateProxyClass()
只知道是用这个方法(有两个实现)生成了一个byte数组,大家可以通过操作IO将这个byte数组写入磁盘,查看代理类生成的方法。
具体怎么实现的,点击去看了一下,层层调用有点复杂,加上编辑这篇文章的时间确实多了点(小半个晚上+午休时间+回审措辞),所以很抱歉,在这就不做展开了,以后有更多的时间,且能力有所长进了再来和大家分享。
当然由于本人水平有限,可能写的不很严谨,会有疏漏或者是错误之处。非常欢迎大家前来批评指正,一定虚心聆听。