JDK动态代理
相信大家都会了解过或用过JDK动态代理,下面附一段经典火车站卖票案例代码作为引入
//卖票接口
interface SellTickets {
void sell();
}
//火车站 火车站具有卖票功能,所以需要实现SellTickets接口
class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代理工厂,用来创建代理对象
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/*
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载
器即可
Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代
理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets sellTickets = (SellTickets)
Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实
例
args : 代理对象调用接口方法时传递的实际参数
*/
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
//执行真实对象
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试类
class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
动态代理类是程序在运行过程中动态的在内存中生成的类。所以要通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)查看代理类的结构,结构如下(为了方便看懂结构,我把代码进行了精简)
//程序运行过程中动态生成的代理类
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3=Class.forName("com.yjq.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null);
}
}
我们可以看到$Proxy0
类是在内存中生成的代理类,实现了SellTickets
接口。
当我们调用代理类的sell方法时,实际执行的是
public final void sell() {
this.h.invoke(this, m3, null);
}
变量 InvocationHandler h是父类里面的成员变量,那我们来看一下父类Proxy
的代码(简化版)
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
看到这,所有逻辑都打通了吧。
总结一下就是:JDK的Proxy类在我们调用方法
newProxyInstance
时,生成一个代理对象在内存中,该对象根据参数实现了SellTickets
接口,重写接口的sell()
方法。在此方法内,调用了我们创建的InvocationHandler
的invoke()
方法。实际调用的就是InvocationHandler
中我们重写的invoke
方法。所以动态代理在运行时会根据我们重写的代码进行增强