1. 什么是

在不改变原始类(或称为被代理类)的情况下,通过引入代理类来给原始类附加功能。

实现方式

基于接口的实现

参照基于接口而非实现编程的设计思想,将原始类对象替换为代理类对象的时候,为了让代码改动尽量少,故代理类和原始类需要实现相同的接口。

基于继承的实现

但是如果原始类没有定义接口,且原始类代码不是我们开发维护的(如可能来自第三方库),我们也无法直接修改原始类,给它重新定义一个接口。这样上面的方法便无法使用。

针对这种无法直接修改原始类的外部类的修改,一般采用继承的方式。

动态代理

静态代理问题

直接创建业务代码的代理类(即静态代理),存在一些问题:
1. 需要在代理类中,将原始类中的所有方法都重新实现一遍,并且为每个方法都附加相似的代码逻辑。
2. 若需要添加的附加功能的类不止一个,需要针对每个类都创建一个代理类。
这将导致项目中类的个数成倍增加,增加了代码维护成倍。并且每个代理类中的代码都有些像模板式的“重复”代码,也增加了不必要的开发成本。

动态代理原理

针对上述问题,我们可以采用动态代理(Dynamic Proxy):不事先为每个原始类编写代理类,而是在运行的时候,动态地创建也是类对应的代理类,然后在系统中用代理类替换掉原始类。

对于Java语言,动态代理的底层依赖的是Java的反射语语法。

  • JDK 的动态代理利用反射机制,动态生成一个实现原始实现的类接口的匿名类,在调用原始类具体方法前调用InvocationHandler来处理。
  • cglib动态代理是利用asm开源包,修改原始类的字节码动态生成原始类的子类来处理。

https://refactoringguru.cn/design-patterns/proxy

适用场景

  1. 业务系统中非功能性需求开发:如监控、统计、鉴权、限流、事务、幂等、日志等功能,可以通过代理模式与业务代码解耦。
  2. RPC 框架也可以看作一种代理模式,被看作是一种远程代理模式:通过远程代理,将网络通信、数据编解码等细节隐藏起来。客户端在使用 RPC 服务的时候,就像使用本地函数一样,无需了解跟服务器交互的细节。
  3. 可用在缓存场景中:如通过某些参数的查询在一定时间内直接返回缓存结果,该功能实现可使用springAOP拦截指定请求等实现。

Java动态代理

实现方式

  1. 创建InvocationHandler(调用处理器)

InvocationHandler 是一个接口,里面只有一个invoke方法。该接口用于实现代理的行为,即用于提供被代理类方法调用发生时所需附加的功能。
当代理的方法被调用时,便会被转发给 InvocationHandler 接口的具体实现类,调用 invoke 方法。此方法中既有附加的新功能,又有被代理类的方法调用。
LoggerDynamicProxyHandler.invoke(Object proxy, Method method, Object[] args)
proxy:动态生成的匿名代理类
method:调用的方法
args:真实主题类method的参数