动态代理

Catalogue
  1. 1.举例
  2. 2.应用场景
  3. 3.静态代理
  4. 4.动态代理技术
    1. JDK 动态代理

1.举例

工厂,商店和客户。买玩具都是从商店买,工厂怎么生产我们不用关心。

这个工厂可以叫做委托类,商店就是代理类,我们就是客户类。

这样的好处是:

  • 隐藏了委托类的实现。
  • 实现客户与委托类之间的解耦,在不修改委托类代码的情况下能够做一些额外的处理(important)

2.应用场景

远程 RPC 调用.通过代理类去实现的.

Spring 的 AOP 切面中我们也是为切面生成了一个代理类.

3.静态代理

定义接口和接口的实现类,然后定义接口的代理对象。将接口的实例注入到代理对象中, 然后通过代理对象去调用真正的实现类,实现过程非常简单也比较容易理解。

静态代理的代理关系在编译期间就已经确定了的。

4.动态代理技术

代理类在程序运行时创建的代理方式被成为 动态代理。

先简回顾一下 JVM 的类加载机制中的加载阶段要做的三件事情:

  • 通过一个类的全名或其它途径来获取这个类的二进制字节流
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在内存中生成一个代表这个类的 Class 对象, 作为方法区中对这个类访问的入口

我们要说的动态代理,主要就发生在第一个阶段。这个阶段类的二进制字节流的来源可以有很多, 比如 zip 包、网络、运行时计算生成、其它文件生成 (JSP)、数据库获取。

其中运行时计算生成就是我们所说的动态代理技术,在 Proxy 类中, 就是运用了 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 —-$Proxy 的代理类的二进制字节流。

所谓的动态代理就是想办法根据接口或者目标对象计算出代理类的字节码然后加载进 JVM 中。

实际计算的情况会很复杂,我们借助一些诸如 JDK 动态代理实现、CGLIB 第三方库来完成的.

另一方面为了让生成的代理类与目标对象 (就是委托类) 保持一致, 我们有 2 种做法:通过接口的 JDK 动态代理 和通过继承类的 CGLIB 动态代理

JDK 动态代理

在 Java 的动态代理中, 主要涉及 2 个类,java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler.

1
2
3
4
5
6
7
8
9
10
public interface InvocationHandler {
/**
* 调用处理
* @param proxy 代理类对象
* @param methon 标识具体调用的是代理类的哪个方法
* @param args 代理类方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

我们对处理类中的所有方法的调用都会变成对 invoke 方法的调用.这样我们可以在 invoke 方法中添加统一的处理逻辑(也可以根据 method 参数判断是哪个方法)

中间类 (实现了 InvocationHandler 的类) 有一个委托类对象引用, 在 Invoke 方法中调用了委托类对象的相应方法,通过这种聚合的方式持有委托类对象引用,把外部对 invoke 的调用最终都转为对委托类对象的调用。

实际上,中间类与委托类构成了静态代理关系.在这个关系中,中间类是代理类,委托类是委托类。