如果是非公有接口,那么会使用和被代理类一样的包名,可以写一个private接口的例子进行一下测试 。
package com.hydra.test.face;public class InnerTest {private interface InnerInterface {void run();}class InnerClazz implements InnerInterface {@Overridepublic void run() {System.out.println("go");}}}这时生成的代理类的包名为com.hydra.test.face,与被代理类相同:

文章插图
- 然后,利用ProxyGenerator.generateProxyClass方法生成代理的字节码数组:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);在generateProxyClass方法中,有一个重要的参数会发挥作用:private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));如果这个属性被配置为true,那么会把字节码存储到硬盘上的class文件中,否则不会保存临时的字节码文件 。- 最后,调用本地方法defineClass0生成Class对象:
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);返回代理类的Class后的流程我们在前面就已经介绍过了,先获得构造方法,再使用构造方法反射的方式创建代理对象 。神秘的代理对象创建代理对象流程的源码分析完了,我们可以先通过debug来看看上面生成的这个代理对象究竟是个什么:

文章插图
和源码中看到的规则一样,是一个Class为$Proxy0的神秘对象,再看一下代理对象的Class的详细信息:

文章插图
类的全限定名是com.sun.proxy.$Proxy0,在上面我们提到过,这个类是在运行过程中动态生成的,并且程序执行完成后,会自动删除掉class文件 。如果想要保留这个临时文件不被删除,就要修改我们上面提到的参数,具体操作起来有两种方式,第一种是在启动VM参数中加入:
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true第二种是在代码中加入下面这一句,注意要加在生成动态代理对象之前:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");使用了上面两种方式中的任意一种后,就可以保存下来临时的字节码文件了,需要注意这个文件生成的位置,并不是在target目录下,而是生成在项目目录下的comsunproxy中,正好和默认生成的包名对应 。
文章插图
拿到字节码文件后,就可以使用反编译工具来反编译它了,这里使用jad在cmd下一条命令直接搞定:
jad -s JAVA $Proxy0.class看一下反编译后$Proxy0.java文件的内容,下面的代码中,我只保留了核心部分,省略了无关紧要的equals、toString、hashCode方法的定义 。public final class $Proxy0 extends Proxy implements Worker{public $Proxy0(InvocationHandler invocationhandler){super(invocationhandler);}public final void work(){try{super.h.invoke(this, m3, null);return;}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}private static Method m3;static {try{m3 = Class.forName("com.hydra.test.Worker").getMethod("work", new Class[0]);//省略其他Method}//省略catch}}这个临时生成的代理类$Proxy0中主要做了下面的几件事:- 在这个类的静态代码块中,通过反射初始化了多个静态方法Method变量,除了接口中的方法还有equals、toString、hashCode这三个方法
- 继承父类Proxy,实例化的过程中会调用父类的构造方法,构造方法中传入的invocationHandler对象实际上就是我们自定义的WorkHandler的实例
- 实现了自定义的接口Worker,并重写了work方法,方法内调用了InvocationHandler的invoke方法,也就是实际上调用了WorkHandler的invoke方法
- 省略的equals、toString、hashCode方法实现也一样,都是调用super.h.invoke()方法

文章插图
为什么要有接口?通过上面的分析,我们已经知道了代理对象是如何生成的了,那么回到开头的问题,为什么jdk的动态代理一定要基于接口呢?
推荐阅读
- g52829航班动态 客机时速
- |“杨三角”新解:企业如何打造动态组织结构?
- 美丽风景图片 唯美风景动态图片
- 清明节|春季钓翘嘴,用这种手竿“动态钓法”,狂拉翘嘴很过瘾!
- 上海|上海昨日新增本土“355+5298” :全域静态管理全员核酸筛查、坚持“动态清零”不放松
- 腾讯游戏|腾讯代理古风手游《云裳羽衣》宣布中止停服!玩家发声感谢中消协
- 车手市场动态:维尔莱茵宣布离开马恒达车队
- 笔记本cpu性能天梯图(速览性能天梯图新动态)
- 大家早上好动态图片(大家早上好图片大全)
- 成绵高速复线(绵广高速复线最新动态)
