深刻理解动态代理

千山鸟飞绝,万径人踪灭。html

孤舟蓑笠翁,独钓寒江雪java

——唐·柳宗元《江雪》git

首发于个人公众号github

深刻理解动态代理bash

1、概述

最近在阅读retrofit源码时,有个关键的所在就是动态代理,细细回想了一下动态代理,发现以前有些细节尚未理解到位,本篇博文将从新深刻理解一下动态代理。ide

2、关于代理

中华名族是一个含蓄的名族,讲究微妙和间接的交流方式。对象之间的间接通讯也是一样是面向对象设计中一条重要的审美观,迪米特法则也指出“一个对象应该对其余对象保持最少的了解”,间接间通讯能够达到“高内聚,低耦合”的效果。
代理是一种重要的手段之一,好比生活中的微商代理,厂家委托其代理销售商品,咱们只跟微商打交道,不知道背后的“厂家是谁”,微商和厂家就能够抽象为 “代理类”和“委托类”,这样就能够,隐藏委托类的实现、实现客户与委托类之间的解耦,能够不用修改委托类的状况下作一些额外的处理函数

2.一、代理模式简介

在《Java与模式》一书中指出"代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的访问",类图以下
post

image.png

经过类图能够发现,代理模式的代理对象 Proxy和目标对象 Subject实现同一个接口,客户调用的是 Proxy对象, Proxy能够控制 Subject的访问,真正的功能实现是在 Subject完成的。

适用场景:学习

  • 不但愿某些类被直接访问。
  • 访问以前但愿先进行一些预处理。
  • 但愿对被访问的对象进行内存、权限等方面的控制。

优势以下ui

  • 代理模式是经过使用引用代理对象来访问真实对象,在这里代理对象充当用于链接客户端和真实对象的中介者。
  • 代理模式主要用于远程代理、虚拟代理和保护代理。其中保护代理能够进行访问权限控制。

2.二、静态代理

“静态”代理,若代理类在程序运行前已经存在,这种一般称为静态代理,好比微商A只代理A品牌的面膜,消费者经过微商才能买到某厂的面膜(控制权),其中微商和工厂都实现了了Sell的接口

委托类面膜工厂

class FactoryOne :SellMask{
    override fun sell() {
        println("FactoryOne: 来自工厂A的面膜")
    }
}
复制代码

微商静态代理

class BusinessAgent : SellMask {
    private lateinit var sellMask: SellMask

    init {
        sellMask = FactoryOne()
    }

    override fun sell() {
        println("BusinessAgent: 微商代理开始在朋友圈打广告")
        sellMask.sell()
        print("BusinessAgent: 赚了一大把")
    }
}
复制代码

共同接口

interface SellMask {
    fun sell()
}
复制代码

2.三、类似模式的比较

既然得到引用就能够作一些扩展之类的事情,这点跟装饰者模式、适配器模式看起来很像,三者都属于结构型模式,可是代理模式核心是为其它对象提供一种代理以控制对这个对象的访问()

代理模式 VS 适配器模式

看上去很像,它们均可视为一个对象提供一种前置的接口,可是适配器模式的用意是改变所考虑的对象的接口,而代理模式并不能改变所代理的对象的接口,这一点上两个模式有着明显的区别,下图分别是 对象适配器和类的适配器UML图

image.png


代理模式VS 装饰模式

装饰者模式与所装饰的对象有着相同的接口,这一点跟代理模式相同,可是装饰模式更强调为所装饰的对象提供加强功能,而代理模式则是对对象的使用施加控制,并不提供对象自己的加强功能;被代理对象由代理对象建立,客户端甚至不须要知道被代理类的存在;被装饰对象由客户端建立并传给装饰对象。装饰者UML图以下

image.png

你觉得这就完了吗?下面👇才是重头戏!

3、深刻理解动态代理

3.一、什么是动态代理

代理类在程序运行时建立的代理方式被成为 动态代理。即代理类并非在代码中定义的,而是在运行时根据咱们在Java代码中"规定"的信息自动生成。静态代理容易形成代码的膨胀,。相比于静态代理, 动态代理的优点在于能够很方便的对代理类的函数进行统一的处理,而不用修改每一个代理类的函数。 仍是以上面的卖面膜微商为例,她在进货市场进行一番比较以后再决定代理哪一个品牌的面膜。

3.二、如何使用动态代理

跟上文同样,微商和面膜工厂都实现了sell接口,这里就不赘述了,下面看下不同凡响的地方,实现动态代理须要实现InvocationHandler接口

实现InvocationHandler接口

public class DynamicProxy implements InvocationHandler {
    private Object object;//被引用的代理

    public Object newProxyInstance(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理商 包装发朋友圈");
        Object result = method.invoke(object,args);
        System.out.println("代理商 赚钱");
        return result;
    }
}
复制代码
  • target 属性表示委托类对象
  • InvocationHandler是负责链接代理类和委托类的中间类必须实现的接口。其中只有一个 invoke函数须要实现
  • invoke函数
public Object invoke(Object proxy, Method method, Object[] args) 复制代码

下面好好看看这个核心函数的参数含义

  • proxy 代经过dynamicproxy.newProxyInstance(business)自动生成的代理类 $Proxy0.class(下文会详细介绍)
  • method表示代理对象被调用的函数,好比sellMask接口里面的sell方法
  • args 表示代理大力调用函数的的参数,这里sell方法无参数

调用代理对象的每一个函数,实际上最终都是走到InvocationHandler的invoke函数,所以能够在这里作一些统一的处理,AOP的雏形就慢慢出现了,咱们也能够根据method方法名作一些判断,从而实现对某些函数的特殊处理。

使用动态代理

fun main(args: Array<String>) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")  //加入这个能够获取代理类

    var maskFactory = FactoryMaskOne()

    var dynamicproxy: DynamicProxy = DynamicProxy()

    var sellMask: SellMask = dynamicproxy.newProxyInstance(maskFactory) as SellMask

    sellMask.sell()

}
复制代码

咱们将委托类面膜工程FactoryMaskOne传到dynamicproxy.newProxyInstance中,经过下面的函数返回了一个代理对象

public Object newProxyInstance(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
复制代码

实际代理类就是在这个时候动态生成的,后续调用到这个代理类的函数就会直接调用invoke函数,让咱们细细看下这个Proxy.newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 复制代码

法的三个参数含义分别以下:

  • loader:定义了代理类的ClassLoder;
  • interfaces:代理类实现的接口列表
  • h:调用处理器,也就是咱们上面定义的实现了InvocationHandler接口的类实例

这里简单总结一下,委托类经过**newProxyInstance **方法获取动态生成的代理类的实例(本例是$Proxy0.class),而后能够经过这个代理类实例调用代理的方法得到委托类的控制权,对代理类的调用实际上都会走到invoke方法,在这里咱们调用委托类的相应方法,而且能够添加本身的一些逻辑,好比统一处理登录、校验之类的。

3.三、动态生成的代理类$Proxy0

在Android Studio中调用不了ProxyGenerator这个类,这个类在sun.misc包中,使用IntelliJ IDE建立java工程,须要看一下jdk的反射中Proxy和生成的代理类$Proxy0的源码,可使用

//生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
复制代码

生成的代理类在com.sun.proxy包里面完整代码以下

public final class $Proxy0 extends Proxy implements SellMask {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
	
    public final void sell() throws {
        try {
          //能够看到接口方法都交由h的invoke方法处理,h在父类Proxy中定义为InvocationHandler接口
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("dev.proxy.SellMask").getMethod("sell");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

复制代码

从生成的代理类中能够看到

  • 动态生成的代理类是以$Proxy为类名前缀,继承自Proxy,而且实现了Proxy.newProxyInstance(…)第二个参数传入的全部接口的类。
  • 接口方法都交由h的invoke方法处理,h在父类Proxy中定义为InvocationHandler接口,为Proxy.newProxyInstance(…)的第三个参数

3.4 动态代理类如何生成

  • 关注点1 Proxy.newProxyInstance(……)函数

动态代理类是在调用 Proxy.newProxyInstance(……)函数时生成的,精简后的核心代码以下

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
        
        final Class<?>[] intfs = interfaces.clone();
        ……
        /* * Look up or generate the designated proxy class. * 获得动态代理类 */
        Class<?> cl = getProxyClass0(loader, intfs);

        /* * Invoke its constructor with the designated invocation handler. */
        try {
            ……
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //而后将InvocationHandler做为代理类构造函数入参新建代理类对象
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
           ……
    }
复制代码

能够看到 首先调用它先调用getProxyClass(loader, interfaces)获得动态代理类,而后将InvocationHandler做为代理类构造函数入参新建代理类对象。

  • 关注点2  Class<?> cl = getProxyClass0(loader, intfs);

如何获取到 生成动态代理类呢,一步步追踪,咱们发现,在Proxy#ProxyClassFactory类中,在ProxyGenerator中去生成动态代理,类名以$Proxy+num做为标记

/*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
复制代码

小结

本篇主要java中的代理模式以及跟其余模式的对比,并重点介绍了JDK中的动态代理机制,像AOP、retrofit核心机制之一就使用到了这种技术,但Java动态代理是基于接口的,若是对象没有实现接口咱们该如何代理呢?那就须要CGLIB了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它容许咱们在运行时对字节码进行修改和动态生成。CGLIB经过继承方式实现代理,这里就不展开赘述了。

参考连接

欢迎关注个人公众号,一块儿学习,共同提升~

相关文章
相关标签/搜索