关于后端:深入Java动态代理源码分析ProxyInvocationHandler

9次阅读

共计 3146 个字符,预计需要花费 8 分钟才能阅读完成。

先来理一下概念实践

代理三要素

形象主题角色(Subject)

具体主题角色(RealSubject)

代理主题角色(Proxy)

代理关系图

栗子

Subject:购房需要

RealSubject:小明的购房需要

Proxy:中介 A(只为小明服务)

中介能够帮忙或者代理小明做一些事件,比方筛选房源、预沟通等等,这就是代理的益处,业余、高效。

然而有以下问题:

1、中介 A 只为小明服务,如果小红、小强都要买房,怎么办呢?

2、小明还有买车需要,也想找中介帮忙,怎么办呢?中介 A 懂房但不懂车。

问题

动态代理 中,此时就须要 new 新的代理类,无论怎么形象、怎么封装,一个代理类总是能力无限的。要为每个指标 Subject 编写对应的 代理类,随着零碎宏大,工作量会剧增,并且可维护性变差。那么思考,面对变动万千的subject,如何能力少写代理类,或者不写代理类,又能实现代理性能呢?

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

这就是动静代理。
《2020 最新 Java 根底精讲视频教程和学习路线!》

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​啊。

源码剖析

上面简略看源码剖析一下。

getProxyClass0(loader, intfs)

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

是的,这就是代理类的外围生成逻辑。

proxyClassCache.get(loader, interfaces)

​ 对代理类的缓存策略,后边就能看进去,这是十分有必要的,这个缓存数据构造相当简单,咱们找到外围的点:

咱们看到 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

​ 而这里的重点是 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 是什么呢?

是的,终于扯到要害了 ​ProxyClassFactory​

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.ProxyMethod#generateMethod

​是的,就是咱们生成字节码的罕用套路。

到这里,就齐全扣上了,再回头看 java 实现的动静代理离不开这几个外围点

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

链接:https://juejin.cn/post/690406…

正文完
 0