文章目录
参考文章
前言
代理可以看做是一种封装, 最终会把功能调用传递给背后真正的实现方。 使用代理的原因往往是因为背后的真实实现方不便于直接被调用(可能是一个远程对象) 或者需要为被代理的功能添加一些辅助功能。
注: 根据 GoF 设计模式原书的描述, 代理模式和装饰器模式的实现方式是完全一样的, 其区别主要在于使用场景
- 代理模式的目的在于进行 访问控制, 其意图在于为被代理对象提供一个替身, 而之所以提供替身而不直接访问的目的是, 原有的被代理对象可能不便于被直接访问(是一个远程对象), 或者是一个访问权限需要被限制的对象。
- 装饰器模式的目的在于进行功能添加 / 功能增强 , 其意图在于为被装饰对象动态地增加功能或属性
笔者认为这种说法是比较合理的, 毕竟一个代理,从业务逻辑角度来讲, 对被代理方应该是透明的。 就好比你委托一个中介替你完成一些事情, 他所达成的结果,应当和你的期待一致, 不应该有差别。所以代理如果要为被代理添加一些功能的话, 也应该是业务逻辑无关的功能,例如日志统计等功能。
动态代理和静态代理的区别:
- 动态代理 中的代理关系是程序运行时产生的, 支持动态变化。 静态代理 的对象间代理关系是编译前就已经确定的, 在运行时刻, 是固定不变的。
- 动态代理可以通过单一的一个类中的一个方法, 为多个类的多个方法提供代理服务。 动态代理允许你编写代码为任意的一种接口提供代理服务。
- 其背后的工作原理,是针对特定接口, 创建代理类,使用者可以在这个代理类 Proxy 身上直接调用各种接口方法, 而代理类会把所有的方法调用都转发给一个单一的
handler
的invoke
方法, 完成真正的逻辑。
虽然动态代理在日常开发中并不经常使用,但是对于框架编写者而言, 动态代理非常实用。
动态代理也是 Spring AOP 的实现基础, 本文旨在梳理一下 Java 内置的动态代理实现方式, 供初学者学习参考
JAVA 原生动态代理
java 自带的动态代理方式是构建在 JDK 中的一个特性, 所以不需要引入任何额外的依赖即可使用。
但是有一个限制: java 内置的动态代理必须依托于接口
jdk 动态代理主要涉及到以下3个概念
- 接口(Interface )
- 这里以一个
FooInterface
进行举例,FooInterface
中有一个foo()
方法必须实现
- 这里以一个
- 代理类实例 ( Proxy Instance)
proxyInstance
是我们通过动态代理创建的代理类实例, 对外表现为实现了FooInterface
接口
- 调用处理器(Invocation Handler)
- 对于
proxyInstance
的方法调用, 会实际触发invocationHandler.invoke()
函数
- 对于
上述三个角色的逻辑工作关系是这样的
- Tips:
invocationHandler.invoke()
方法, 常常会把针对 proxyInstance 的调用进一步转发给其他类,这其实是引入了多层代理关系, 即 interface --> invocationHandler–> interfaceImpl 这有时会让部分读者产生困惑, 搞不清动态代理的核心工作机制。 - 实际上, 读者仅需要记住, 动态代理本身最基础的逻辑就是完成一层代理关系, 即将针对某一接口的调用, 全部转发给一个 InvocationHandler 接口实现类的 invoke 方法。
- 下面的例子一和例子二, 分别展示了单层代理关系与多层代理关系的实现方式。
例子一:一个只会打印被调用方法的代理
下面通过一个例子进一步展现使用流程。 我们的目标是创建一个 Map 接口的代理类实例 fakedHashMap, 调用代代理类所实现的 Map
接口中的 get(),
put()
方法时, 什么都不执行, 仅仅打印被执行的方法名称
- 接口(Interface ) -->
Map
- 被代理类(Target Class) -->
无
- 代理类实例 ( Proxy Instance) -->
fakedMap
- 调用处理器(Invocation Handler) -->
MapInvocationHandler
步骤一: 创建 Invocation Handler
InvocationHandler
的 invoke
public class DynamicInvocationHandler implements InvocationHandler