在另一篇文章浅析AOP实现原理(2)JDK动态代理中我们了解了JDK动态代理的用法,但是光会用还不行,这篇文章我们来探讨几个第一次使用JDK动态代理时可能都会产生的疑问:
- 1、代理类对象究竟是如何生成的
- 2、invoke方法是何时被调用的
JDK如何动态生成代理类对象
在上一篇文章中,生成代理的方法为:
public static Object getProxy(Object object){
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
new ProxyClient(object));
}
我们来看看这个方法是如何实现的(以下非完整代码,只展示了部分重要代码)
//这个方法中传入了代理对象的类加载器,接口和相应的InvocationHandler对象(最终调用者触发其中的Invoke方法):
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler )throws IllegalArgumentException
{
//复制接口
final Class>[] intfs = interfaces.clone();
//生成代理类
Class> cl = getProxyClass0(loader, intfs);
//根据构造函数来创建代理对象
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
}
由此可知newProxyInstance
函数做了两件事:生成代理类的Class对象,然后用Class对象的构造函数创建代理对象,我们先来看看getProxyClass0
方法是如何生成代理类的
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);
}
根据源码中的注释可知,JAVA使用WeakCache
对代理类进行了缓存,该缓存的定义如下
private static final WeakCache[], Class>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
具体的缓存机制我们不去研究,只需要知道它将首先从缓存中查找是否存在,若未找到,会通过ProxyClassFactory
来创建代理类。行,那我们就再来看看ProxyClassFactory
的代码
private static final class ProxyClassFactory
implements BiFunction[], Class>>
{
//所有代理类的前缀
private static final String proxyClassNamePrefix = "$Proxy";
//代理类类名后缀,是AtomicLong类型的唯一计数
private static final AtomicLong nextUniqueNumber = new AtomicLong();
//重写BiFunction接口的方法
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
//创建一个IdentitiHashMap用来存放获取的类
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class> intf : interfaces) {
Class> interfaceClass = null;
//根据接口的名称获取类
interfaceClass = Class.forName(intf.getName(), false, loader);
//利用IdentityHashMap的特性判断是否有重复的类
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//定义代理类包名
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");
}
}
}
//默认包名为com.sun.proxy
if (proxyPkg == null) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//确定代理类名,默认为com.sun.proxy.$Proxy0(第二个代理类后缀为1,以此类推)
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成代理类的二进制文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
//返回代理类
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
}
由此可知,ProxyClassFactory
实现了BiFunction
接口,将类加载器和接口数组传入重写的apply()
方法中,其中主要做了三件事:
- 1、生成代理类的名称,默认为
com.sun.proxy.$Proxy0
(数字标识不同的类,如$Proxy1,$Proxy2) - 2、通过
ProxyGenerator.generateProxyClass()
方法生成代理类的二进制文件 - 3、通过
defineClass0()
方法产生对应名称的Class对象并返回
因此ProxyClassFactory
中我们主要关注生成二进制文件的ProxyGenerator.generateProxyClass()
方法:
//传入代理类名proxyName->var0,接口对应的Class对象interfaces->var1,标识接口是否为共有接口的accessFlags->var2
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {
//生成ProxyGenerator对象
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
//调用generateClassFile()方法生成类的二进制文件
final byte[] var4 = var3.generateClassFile();
//如果saveGeneratedFiled为true,将文件写入硬盘的IO操作
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
});
}
return var4;
}
所以在ProxyGenerator.generateProxyClass()
方法中,首先调用了var3.generateClassFile()
方法产生二进制文件,然后将该文件写入硬盘或直接返回,那我们再来关注一下var3.generateClassFile()
(我保证这是最后一次)
private byte[] generateClassFile() {
//将hashCode、equals、toString方法添加到proxyMethods这个map中
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
//获取接口类和个数
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//遍历所有的接口
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
//获得该接口中所有的方法
Method[] var5 = var4.getMethods();
int var6 = var5.length;
//将方法添加到代理方法
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11,
//中间省略验证步骤
Iterator var15;
//生成代理类的构造函数并添加到methods,methods是一个List,它包含了代理类的方法信息
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
//将proxyMethod里的方法和变量存入fields和methods里
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
//一些初始化
this.methods.add(this.generateStaticInitializer());
//后面主要是一些将methods和fields写成二进制流的文件操作
·······
return .......toByteArray();
}
}
至此我们终于得到了代理类的二进制文件,再来理一理思路:
首先Proxy.newProxyInstance()
方法被调用,在该方法中,getProxyClass()
方法被调用,在getProxyClass
方法中先查找缓存中是否有代理类,若没有,则调用ProxyClassFactory
的apply()
方法来产生代理类,apply()
方法中先根据自定义的规则生成包名和类名进行拼接,然后调用ProxyGenerator.generateProxyClass()
方法生成代理类的二进制文件,而真正生成二进制文件的方法是其中的var3.generateClassFile()
方法,该方法中遍历所有接口,获得接口中的方法和变量信息,最后根据这些信息写成二进制文件,根据saveGeneratedFileds
判断是否写入硬盘,二进制文件返回到ProxyClassFactory
的apply()
方法中,再根据包名,类加载器,代理类的二进制文件生成代理类并返回。
看起来有点混乱,其实用一句话来说就是:通过java反射从接口中获取变量和方法信息来生成代理类的过程
invoke方法是何时被调用的
要知道invoke何时被调用,就必须先知道jdk动态代理为我们生成的文件究竟是什么,以下代码在指定路径输出文件:
@Test
public void testProxy() throws IOException {
String path = "D:/$Proxy1.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", KillerImpl.class.getInterfaces());
FileOutputStream out = new FileOutputStream(path);
out.write(classFile);
out.flush();
}
到D盘下用反编译工具获得Proxy1的源文件(不会反编译的点这里):
import com.lwl.aop.Killer;
import java.lang.reflect.*;
public final class $Proxy1 extends Proxy
implements Killer
{
//调用Proxy的构造函数注入invocationHandler
public $Proxy1(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
//的kill方法在此处被调用
public final void kill()
{
try
{
//调用Proxy中invocationHandler的invoke方法
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.lwl.aop.Killer").getMethod("kill", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
这个文件是继承自Proxy
并实现了Killer
的类,注入了invocationHandler
,包含equals
、toString
、hashCode
、kill
四个方法,在各个方法内部调用Proxy
的InvocationHandler
中的invoke
方法,至此我们终于弄明白了JDK动态代理的实现原理
部分参考JDK动态代理实现原理