栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JDK Proxy使用、代理类执行逻辑、获取代理类源码流程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JDK Proxy使用、代理类执行逻辑、获取代理类源码流程

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获取代理类的全过程

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/750110.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号