JDK Proxy使用以及获取代理类的原理
JDK Proxy原理Proxy类
使用栗子
原理剖析
源码剖析
Proxy类相信大家用的最多的就是:
T proxyInstance = (T)Proxy.newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
参数:一个类加载器对象、大于等于一的接口Class对象、InvocationHandler实现类对象
返回:继承Proxy实现所有interfaces接口的代理对象
所以JDK Proxy既可以代理接口的实现类对象(通过实现类对象获取其类加载器和接口数组,invoke时通过Method传入对象和参数反射调用),也可以直接代理接口(通过类文件获取其类加载器和接口数组,invoke时根据Method方法名执行对应逻辑)
使用栗子为了减少类我使用直接代理接口的形式
卖票接口(SellTickets)
public interface SellTickets {
void sell();
}
获取卖票接口代理类的代理工厂类(ProxyFactory)
public class ProxyFactory{
public SellTickets getSellTicketsProxy() {
SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(
SellTickets.class.getClassLoader(),
new Class[]{SellTickets.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = doInvoke(method,args);
after();
return result;
}
private Object doInvoke(Method method, Object[] args) {
if (method.getName().equals("sell")){
System.out.println("售票点卖票");
}
return null;
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
});
return proxyInstance;
}
}
测试类(Client)
public class Client {
public static void main(String[] args) {
SellTickets sellTicketsProxy = new ProxyFactory().getSellTicketsProxy();
sellTicketsProxy.sell();
}
}
执行结果
before 售票点卖票 after原理剖析
通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)查看代理类的结构
首先修改测试类(Client)代码:打印代理类全类名,并让线程阻塞,然后运行
public class Client {
public static void main(String[] args) throws IOException {
SellTickets sellTicketsProxy = new ProxyFactory().getSellTicketsProxy();
sellTicketsProxy.sell();
System.out.println(sellTicketsProxy.getClass());
System.in.read();
}
}
使用Arthas获取内存中的代理类
删除不必要的代码(equals、toString、异常处理…)后结果如下:
public final class $Proxy0
extends Proxy
implements SellTickets {
private static Method m3;
//invocationHandler:我们自己的InvocationHandler实现类
public $Proxy0(InvocationHandler invocationHandler) {
//调用父类Proxy的构造器将invocationHandler赋值给h变量
super(invocationHandler);
}
static {
//加载SellTickets的sell方法的Method对象
m3 = Class.forName("io.github.yuriua.pattern.proxy.dynamic_proxy.example.SellTickets").getMethod("sell", new Class[0]);
}
public final void sell() {
//调用invocationHandler的invoke方法(参数1:当前代理对象,参数2:Method对象,参数3:用户调用传参)
this.h.invoke(this, m3, null);
}
}
//这里的invocationHandler实际上是
invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = doInvoke(method,args);
after();
return result;
}
private Object doInvoke(Method method, Object[] args) {
if (method.getName().equals("sell")){
System.out.println("售票点卖票");
}
return null;
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
}
执行流程如下:
1. 在测试类中通过代理对象调用sell()方法 2. 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法 3. 代理类($Proxy0)中的sell()方法中又调用了InvocationHandler接口的子实现类对象的invoke方法 4. 如果代理的是接口:按照自己逻辑处理即可;如果代理的是对象:method.invoke(对象,参数...)调用真实对象的方法源码剖析
首先调用Proxy.newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
进入newProxyInstance(…)
来到getProxyClass0(loader, intfs),这个函数执行完毕后就会生成代理类
继续进入proxyClassCache.get(loader, interfaces)
从缓存获取supplier,如果supplier为null,初始化factory,将factory赋值给supplier并加入到缓存
进入supplier.get(),其实就是Factory的get方法,因为Factory实现了Supplier
进入valueFactory.apply(key, parameter)
ProxyClassFactory实现了BiFunction(给两个参数返回一个值)
前面逻辑无非就是验证类是否是接口、接口不能重复,以及接口是否公共,如果不是公共的,就把包名设置为同一个包,否则就是com.sun.proxy. 这种包名,名字就是$Proxy加个CAS获取的数字编号,例如com.sun.proxy.$Proxy0
我们来到ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags),看函数返回值和函数名就知道肯定是要生成代理类class字节流了
我们点进去
进入var3.generateClassFile()
可以看到还是非常复杂的过程,就是在写字节码在内存中构建一个代理类的字节流
返回字节流后后通过native方法defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)构建类
我们回到最开始的newProxyInstance(…)
来到cons.newInstance(new Object[]{h})
这里就是用代理类的一个参数构造器为父类(Proxy)的InvocationHandler赋值
众所周知,代理类调用方法都是调用的父类InvocationHandler h中h.invoke(…)方法,参考原理剖析
以上就是Proxy获取代理类的全过程



