美文网首页@IT·互联网
深入Java-动态代理+源码分析Proxy、Invocation

深入Java-动态代理+源码分析Proxy、Invocation

作者: Rflyee | 来源:发表于2020-12-08 20:43 被阅读0次

先来理一下概念理论

代理三要素

抽象主题角色(Subject)
具体主题角色(RealSubject)
代理主题角色(Proxy)

代理关系图

image.png

栗子

Subject:购房需求
RealSubject:小明的购房需求
Proxy: 中介A(只为小明服务)

中介可以帮助或者代理小明做一些事情,比如筛选房源、预沟通等等,这就是代理的好处,专业、高效。

但是有以下问题:
1、中介A只为小明服务,如果小红、小强都要买房,怎么办呢?
2、小明还有买车需求,也想找中介帮忙,怎么办呢? 中介A懂房但不懂车。

问题

静态代理中,此时就需要new 新的代理类,无论怎么抽象、怎么封装,一个代理类总是能力有限的。
要为每个目标Subject编写对应的代理类,随着系统庞大,工作量会剧增,并且可维护性变差。
那么思考,面对变化万千的subject,如何才能少写代理类,或者不写代理类,又能完成代理功能呢?

思考方向:
一个接口(subject) --》如何得到一个代理类proxy? 这不是自动生成类吗?
是的,反射、Class

这就是动态代理。

Java的解决方案,动态代理

Java用jdk提供的ProxyInvocationHandler结合实现动态代理功能。
先来看一段使用栗子

// 获取 买房需求 的Class
Class<?> proxyClass = Proxy.getProxyClass(BuyHouse.class.getClassLoader(), BuyHouse.class);

// 通过Class的构造器Constructor 动态创建统一的代理服务Lianjia
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
BuyHouse lianjia = (BuyHouse) constructor.newInstance(new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 这里就可以随意添加服务了
        // 根据不同人的需求,筛选房子
        XiaomingBuyHouse xiaomingBuyHouse = new XiaomingBuyHouse();
        System.out.println("Lianjia帮每个人筛选好房子");

        // RealSubject,真实客户的看房行为
        Object result = method.invoke(xiaomingBuyHouse, args);

        // 有了这个动态代理的功能,相当于能自动动态生成符合每个人需求的独立的代理服务
        // 这样单个中介 变成了 中介公司。效率更高、服务更好了
        return result;
    }
});

lianjia.seekHouse();

// 简写
BuyHouse lianjia2 = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new Class[]{ BuyHouse.class }, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 进行代理
        return null;
    }
});
lianjia2.seekHouse();

可以看到,java实现的动态代理离不开这几个核心点

1、Proxy.getProxyClass 或者 Proxy.newProxyInstance
2、InvocationHandler#invoke
3、反射

动态代理里,反射是贯穿始终的。

大家可能会奇怪, 一个 ​Proxy​ 一个​InvocationHandler​,底层到底做什么了,怎么就能代理了呢?我们也没看到调用 ​InvocationHandler​的关键方法 ​invoke​啊。

源码分析

下面简单看源码分析一下。

java.lang.reflect.Proxy#newProxyInstance
getProxyClass0(loader, intfs)

我们重点关注参数里的interfacesinvocationHandle,无论是 Proxy.​newProxyInstance()​方式 还是 ​getProxyClass()​方式,重点都落在了​getProxyClass0(loader, intfs)​

是的,这就是代理类的核心生成逻辑。

proxyClassCache.get(loader, interfaces)
java.lang.reflect.Proxy#getProxyClass0


对代理类的缓存策略,后边就能看出来,这是非常有必要的,这个缓存数据结构相当复杂,我们找到核心的点:

java.lang.reflect.WeakCache#get

我们看到proxyClassCache.get(loader, interfaces),无论如何缓存,找return就对了。

if (supplier != null) {
    // supplier might be a Factory or a CacheValue<V> instance
    V value = supplier.get();
    if (value != null) {
        return value;
    }
}

supplier.get()

然后就是 ​supplier.get()​,也就是 ​java.lang.reflect.WeakCache.Factory#get

java.lang.reflect.WeakCache.Factory#get

​ 而这里的重点是 value = Objects.requireNonNull(valueFactory.apply(key, parameter));

注:Objects.requireNonNull()返回值 还是参数本身哈,仅仅是进行非空判断,

public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

所以这里的重点就是 valueFactory.apply(key, parameter);
valueFactory是什么呢?

java.lang.reflect.WeakCache#WeakCache

image

是的,终于扯到关键了 ​ProxyClassFactory​

java.lang.reflect.Proxy.ProxyClassFactory#apply
java.lang.reflect.Proxy.ProxyClassFactory#apply

到这里基本理顺了,所以动态代理的核心,还是利用上面讲到的反射等技术,动态生成代理类的过程。

ProxyClassFactory#apply方法里省略里很多逻辑,大家可以展开一下,肯定会似曾相识。

比如:

  1. 在代码中可以看到JDK生成的代理类的类名是“$Proxy”+序号。
  2. 如果接口是public的,代理类默认是public final的,并且生成的代理类默认放到com.sun.proxy这个包下。
  3. 如果接口是非public的,那么代理类也是非public的,并且生成的代理类会放在对应接口所在的包下。
  4. 如果接口是非public的,并且这些接口不在同一个包下,那么就会报错。
sun.misc.ProxyGenerator.ProxyMethod#generateMethod

如果要继续深入追寻 生成的 代理类 和 ​InvocationHandler​的​invoke​的关系,继续往里看两层,就是了👇

sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)

sun.misc.ProxyGenerator#generateClassFile


sun.misc.ProxyGenerator.ProxyMethod#generateMethod

sun.misc.ProxyGenerator.ProxyMethod#generateMethod

​是的,就是我们生成字节码的常用套路。

到这里,就完全扣上了,再回头看 java实现的动态代理离不开这几个核心点

1、Proxy
2、InvocationHandler
3、反射技术

参考
https://www.zhihu.com/question/20794107
https://www.cnblogs.com/liuyun1995/p/8157098.html

相关文章

  • 深入Java-动态代理+源码分析Proxy、Invocation

    先来理一下概念理论 代理三要素 抽象主题角色(Subject)具体主题角色(RealSubject)代理主题角色(...

  • java随笔(十一)

    java动态代理源码分析,总结。java动态代理实现步骤: 通过阅读源码发现,动态生成代理对象$Proxy0,该对...

  • 动态代理

    动态代理源码分析 动态代理2

  • Java-反射-动态代理

    概述 Proxy 提供静态方法来创建动态代理类和动态代理对象; 源码基于 Android-SDK-29 中的 JD...

  • Java--动态代理Proxy源码分析

    java的动态代理通过Proxy的newProxyInstance方法来创建代理对象 从newProxyInsta...

  • IOC依赖注入框架原理

    1 动态代理深入 首先简单看下一个动态代理的例子 当通过Proxy的newProxyInstance方法创建一个I...

  • Java代理

    前言 Java代理大致可以分为静态代理(static proxy),动态代理(dynamic proxy)。所谓代...

  • Retrofit2源码阅读

    本次分析的要点: Retrofit中的动态代理和整个流程(Proxy、ServiceMethod、OkHttpCa...

  • GCLIB动态代理

    1,简介 1)对比JDK动态代理,由于JDK动态代理生成的proxy对象,都是继承了Proxy类,并实现了所代理的...

  • Java动态代理原理剖析(一)

    这篇文章将深入的分析Java的动态代理实现原理,首先将自己实现一个动态代理,然后顺势分析JDK中的动态代理实现并与...

网友评论

    本文标题:深入Java-动态代理+源码分析Proxy、Invocation

    本文链接:https://www.haomeiwen.com/subject/kldqgktx.html