- 前言
- 代理模式的结构
- JDK 动态代理
- 1、定义抽象主题类
- 2、定义具体主题实现类
- 3、编写代理类工厂,用于生成动态代理对象
- 4、编写消费者
- JDK的动态代理源码分析
- ProxyGenerator.generateProxyClass 做了什么
- 资料参考
- 测试代码下载
在上一篇博客中,重点说明了代理模式的概念、结构和案例等。
代理模式的结构链接地址:结构型模式——代理模式之静态代理
代理模式中,分为以下三种角色:
- 抽象主题类(Subject):
定义规范!
通过接口或者抽象类声明真实主题和代理对象实现的业务方法。 - 真实主题类(Real Subject):
实现抽象主题中的具体业务,是代理对象所代表的真实对象,即最终需要引用的对象。
- 代理类(Proxy):
提供了与真实主题相同的接口,其内部包含有对真实主题的引用。它可以访问、控制或扩展真实主题的功能。
代理模式一般分为静态代理和动态代理两种方式。其根本区别在于代理类一个是编译期生成,而另一个是在程序执行期间生成。
动态代理一般有JDK代理和CGLib代理两类。接下来详细说明这两种方式产生动态代理的区别。
Java中提供了一个动态代理类java.lang.reflect.Proxy,其中提供有一个newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)方法,用来获取代理对象。
动态代理和静态代理的主要区别,在于代理类对象的生成方式!
静态代理就是编写代码,根据编写的代码在程序编译期间生成!
动态代理,则是依据 Proxy 代理工具类,依据其中给定的方法自动去生成!
接下来依旧采取代码案例的方式,进行使用说明。
1、定义抽象主题类抽象主题类,其实就是一种约束(或者叫规范)。
package proxy.jdk;
public interface SellPhone {
// 定义约束,代理对象和具体主题对象,都有卖的操作
void sell();
}
2、定义具体主题实现类
这里还是依据华为厂商为例。
package proxy.jdk;
public class HWContractor implements SellPhone {
@Override
public void sell() {
System.out.println("华为厂商卖手机");
}
}
3、编写代理类工厂,用于生成动态代理对象
在该小节之前就说到,JDK实现动态代理的方式,是采取动态代理类java.lang.reflect.Proxy,中的newProxyInstance方法,用来获取代理对象。
其中该方法具有三个参数信息。
这三个参数的含义如下所示:
| 名称 | 含义 |
|---|---|
| ClassLoader | 类加载器,用于代理类的加载方式。可以通过目标对象(具体主题实现类)获取类加载器。 代理类和具体主题实现类都是抽象主题类的子类 |
| Class>[] | 代理类实现的接口的字节码对象。 |
| InvocationHandler | 代理对象的调用处理程序。 |
其中newProxyInstance中的InvocationHandler接口中定义的invoke方法,也存在三个参数:
其各个参数的含义如下所示:
| 名称 | 含义 |
|---|---|
| proxy | 代理对象,与Proxy.newProxyInstance返回的对象是同一个对象。 |
| method | 对接口中的方法进行封装的Method对象。 |
| args | 调用方法的实际参数。 |
根据上述说明,编写动态代理工厂实现类:
package proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
// 声明目标对象
private HWContractor hwContractor = new HWContractor();
// 提供获取代理对象的方法
public SellPhone getProxyObj(){
// 使用 Proxy 获取代理对象
SellPhone proxyInstance = (SellPhone) Proxy.newProxyInstance(
hwContractor.getClass().getClassLoader(),
hwContractor.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强
System.out.println("代售点赚取手续费");
// 执行目标对象的方法
Object obj = method.invoke(hwContractor, args);
return obj;
}
});
return proxyInstance;
}
}
4、编写消费者
package proxy.jdk;
public class Client {
public static void main(String[] args) {
// 1、创建代理类工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 2、通过自定义的代理工厂中的方法,创建出代理对象
SellPhone proxyObj = proxyFactory.getProxyObj();
// 3、使用代理对象的方法
proxyObj.sell();
}
}
JDK的动态代理源码分析
在动态代理中,使用Proxy.newProxyInstance创建一个SellPhone 接口的子类,由代理模式的结构可知:
1、具体主题类是SellPhone的子类。
2、代理类是SellPhone的子类。
那么,采取Proxy.newProxyInstance则是一种自动生成代理类的方式。
进入源码中可以看出:
此处逻辑表示自动生成代理类。
Class> cl = getProxyClass0(loader, intfs);
继续进入其中,或者采取debug运行可知,其生成代理类的实际操作如下所示:
采取从一个缓存中获取,其具体逻辑又是怎么操作的呢,继续向下看:
根据debug方式运行,发现此处subKeyFactory.apply(key, parameter)执行的并非是默认的apply方式,而是静态内部类 ProxyClassFactory中apply方法。
从该方法的逻辑中可以看出:
最后采取defineClass0生成一个代理类对象信息。这个方法是一个native修饰的方式。可以采取将其生成的信息保存至文本中进行查看。
编写代码,将该数组信息保存至文本中。
import java.io.IOException;
import sun.misc.ProxyGenerator;
import java.nio.file.Files;
import java.io.File;
import java.nio.file.Path;
import java.lang.reflect.Modifier;
public class TestFile {
public static void main(String[] args)throws IOException {
// 复制源码中java.lang.reflect.Proxy.ProxyClassFactory.apply,debug出的数据信息
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
String proxyName = "proxy0";
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, new Class[] {SellPhone.class}, accessFlags);
// 采取流的方式保存至文本
File file = new File(System.getProperty("user.dir") + "/src/proxy/jdk/proxy0.class");
//必须保证父目录的存在
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();//不存在 则创建父目录
}
Path path = file.toPath();
System.out.println(path);
Files.write(path, proxyClassFile);
}
}
运行后,在对应的包中可以看到具体的文件信息:
提取其中与代理类相关的代码,如下所示:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.jdk.SellPhone;
public final class proxy0 extends Proxy implements SellPhone {
private static Method m3;
static{
m3 = Class.forName("proxy.jdk.SellPhone").getMethod("sell");
}
public final void sell() throws {
// 调用父类的InvocationHandler中的invoke方法
// 传递参数为null
super.h.invoke(this, m3, (Object[])null);
}
}
从中可以看出,真正的逻辑很明了
代理模式类,也是SellPhone的子类,作为子类实现父类接口其子类必须重写抽象方法,
所以,上述代码中sell()为代理类的具体实现方式。其逻辑为super.h.invoke,调用父类中的 h 类的 invoke方法。
父类中的 h.invoke 为程序代码编写中的:
最后采取获取反射到的类的普通方法,执行其具体逻辑。
反射的基本用法——第六点
测试代码下载本篇博客下载地址



