上节讲到动态代理生成的类为 $Proxy0,但是在我们项目里面却不存在,实际我们是用了这个实现类调用了方法,想要知道这个问题,首先要理解类的完整生命周期.
Java 源文件:即我们在 IDE 里面写的.java 文件
Java 字节码:即编译器编译之后的.class 文件(javac 命令). 备注:Java 代码为何能够跨平台,和 Java 字节码技术是分不开的,这个字节码在 windows, 在 linux 下都是可以运行的
class 对象:工程启动的时候 classLoader 类加载器会扫描这些字节码并加载到 classLoader 上面生成 class 对象,有了类对象,便可以 new 实例了。(class 对象保存在方法区元空间 JDK1.8)
卸载:垃圾回收,关于回收机制,算法有兴趣可以去了解。class 对象什么时候被回收?答:可达性分析,当发现某个类不被引用,类会被回收
类的生命周期与动态代理关系 动态代理是没有 Java 源文件,直接生成 Java 字节码的,加载到 JVM 上面的。字节码来源于内存,比如 tomcat 的热加载就是从网络传输过来的。既然是直接生成的 Java 字节码,是怎么生成的?从源码开始分析,从 Proxy.newProxyInstance 方法开始看。
Class<?> cl = getProxyClass0(loader, intfs); 这行代码生成了.class 字节码并且生成了 class 对象,然后拿这个类对象获取构造函数,再 newInstance,生成实例对象,是通过反射的机制。重点还是怎么生成.class 字节码。
接下来 apply() 方法往下看
生成了字节码数组,从而生成了 Java 字节码,defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length) 则是加载字节码文件,此方法为 native 方法,C 语言方法,操作系统类库(C/C++/ 汇编)。
字节码文件的结构是如何的呢?我们把 class 文件生成出来并在反编译工具打开, 这里就用到了源码里面的方法了。生成.class 文件的代码如下:
public static void generateClass(String proxyName, Class[] paramArrayOfClass, Class clazz) throws IOException {
byte[] classFile=ProxyGenerator.generateProxyClass(
proxyName, paramArrayOfClass);
String path=clazz.getResource(“.”).getPath();
System.out.println(path);
FileOutputStream outputStream =null;
outputStream = new FileOutputStream(path + proxyName + “p.class”);
outputStream.write(classFile);
outputStream.flush();
outputStream.close();
}
在刚刚的动态代理测试类增加几行代码:
public static void main(String[] args) throws IOException {
// 代购公司 C,负责代购所有产品
DynamicProxyCompanyC proxy = new DynamicProxyCompanyC();
// 日本有家 A 公司生产男性用品
ManToolFactory dogToolFactory = new AManFactory();
// 代购 A 公司的产品
proxy.setFactory(dogToolFactory);
// 创建 A 公司的代理对象
ManToolFactory proxyObject = (ManToolFactory) proxy.getProxyInstance();
// 代理对象完成代购男性用品
proxyObject.saleManTool(“D”);
System.out.println(“————–“);
// 日本有家 B 公司生产女性用品
WomanToolFactory womanToolFactory = new BWomanFactory();
// 代购 B 公司的产品
proxy.setFactory(womanToolFactory);
// 创建 B 公司的代理对象
WomanToolFactory proxyObject1 = (WomanToolFactory) proxy.getProxyInstance();
// 代理对象完成代购女性用品
proxyObject1.saleWomanTool(1.8);
// 生成代理类的.class 文件
DynamicProxyCompanyC.generateClass(proxyObject1.getClass().getSimpleName(),
womanToolFactory.getClass().getInterfaces(), womanToolFactory.getClass());
}
根据打印出来的 class 文件路径打开并在反编译工具上打开
动态代理生成的类就是这个了,调用业务方法 saleWomanTool 实际上变成了这个 h.invoke,而这个 h 是所有 Proxy 类里面含有的 protected InvocationHandler h;(源码可见),在用 Proxy 创建代理实例的时候已经传入过了。
所以调用方法 saleWomanTool 就有了前置增强和后置增强。到这里已经解开了动态代理的原理