背景
最近像研究下spring 原理,看到代理这块,想去看看JDK动态代理产生的过程,这里想要知道最终生成代理类的结构,于是需要知道class 文件的结构。
解决办法:
产生jdk 代理文件的方法
- 方法一:在调用测试代理的方法前加(此方法通知虚拟保存产生的代理文件):
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
运行后会在项目的更目录下生成.class 文件,见:
- 方法二:将.class 二进制数据写到本地文件中,然后用反编译器(jd)打开就看到了。
调式Proxy 类是发现它调用
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
这个类的方法产生二进制数据,并且它还有一个 public static byte[] generateProxyClass(String var0, Class<?>[] var1) 静态方法根据名称和接口对应的Class 产生二进制数组,这个就是代理类对应的.class的二进制数据,如果将这些数据写成二进制保存就好了,代码如下:
public static void testProxyGenetate() {
byte[] newProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", PersonServiceImpl.class.getInterfaces());
System.out.println(newProxyClass);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File("/Users/shenjin/Desktop/$Proxy0.class"));
try {
fileOutputStream.write(newProxyClass);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
这里注意文件名$Proxy0.class 和 generateProxyClass 传入的名称参数要相同(我一开始传的不同导致产生的文件无法打开),因为java要求类名和类的文件名相同。
输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");
产生的代理类结果
package com.leran;
import com.learn.proxy.PersonService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class proxy01 extends Proxy implements PersonService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public proxy01(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final boolean saveUser(String var1) throws {
try {
return (Boolean)super.h.invoke(this, m3, new Object[]{var1});
} 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 getUserName() throws {
try {
return (String)super.h.invoke(this, m4, (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);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.learn.proxy.PersonService").getMethod("saveUser", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.learn.proxy.PersonService").getMethod("getUserName");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
结论
jdk 代理并不神秘,基本原理就是:
- 获取需要代理的接口的Class 对象信息
- 生成一个新的.class文件,这个文件就是根据.class文件的生成规则动态的产生一个基于二进制格式的数组,数组本质就是.class 的二进制数组,它继承自Proxy类实现了代理接口,并且加入了通用的HashCode方法,toString,equals三个方法。
- Proxy 调用defineClass0 这个native方法基于二进制数组产生一个Class 对象的实例,即代理对象的Class.
- 调用cl.getConstructor(constructorParams).newInstance(new Object[]{h});返回代理类。
- 对代理类方法的调用都会调用super.h.invoke(this, m4, (Object[])null); 因此可以在Invoke方法中进行各种拦截操作。