一、概述
1.1 定义
Proxy Design, 在不改变原始类(被代理类)的情况下,通过代理类给原始类添加一些附加功能。
1.2 解决的问题
存在的问题:业务代码和非业务功能,放在一个方法中。一方面违背了单一职责原则
;另一方面,未来新增其他非业务功能时,需要修改源代码,违背了开闭原则
,同时代码的可读性,非业务扩展性都不好。
代理的优势:解决了上述场景下存在的问题,将业务(核心)功能和非业务功能解耦,降低耦合度,符合单一职责和开闭原则,代码的扩展性和可读性增强。
二、 实现原理
2.1 整体链路图
2.2 代理类型
代理模式类别 | 定义 | 特点 | 运行阶段 | 优缺点 |
---|---|---|---|---|
静态代理 | 实现同一接口的代理类 | 针对本项目包中的接口/组合模式 | 编译期 | 需要为每个类增加代理类,导致类的个数极速增加,同时扩展性不强 |
静态代理 | 继承原始类的代理类 | 针对第三方包,无法修改第三方包的类/继承模式 | 编译期 | 需要为每个类增加代理类,导致类的个数极速增加,同时扩展性不强 |
动态代理 | JDK代理类 | 只针对有接口的实现类 | 运行期 | 可以动态为每个类生成代理类 |
动态代理 | Cglib代理类 | 针对非抽象类的继承类 | 运行期 | 可以动态为每个类生成代理类 |
模式 | 优缺点 | 应用场景 |
---|---|---|
组合模式 | 优点:组合模式是基于接口来实现的,因此可以对所有的子类进行扩展,而不需要添加额外的代码,扩展性好 缺点:需要为接口所有的方法做代理 | 针对接口的代理 |
继承模式 | 优点:只需要针对需要扩展的方法进行代理 缺点:只能针对单一父类进行扩展 | 针对非抽象类的代理 |
2.3 实现原理
2.3.1 JDK代理
核心原理: 字节码重组技术
- 通过反射获取被代理类的引用,并获取所有的被代理类的接口;
- JDK代理类重新生成一个类,该类要实现被代理类的接口;
- 动态的生成java代码,新增加的非业务逻辑由自定义的逻辑代码实现;
- 编译新生成的.java代码的.class文件;
- 重新加载到JVM中执行。
2.3.2 Cglib代理
核心原理: 主要通过Enhance来设置superCls以及callback来实现
2.3.3 两者区别
代理类别 | 运行阶段 | 技术手段 | 执行方式 |
---|---|---|---|
JDK代理 | 运行期 | 直接重写字节码 | 底层采用反射的方式调用,执行效率低 |
cglib代理 | 运行期 | 通过ASM框架重写字节码,比JDK代理效率低 | 采用FastClass方式执行,效率更高 |
三、使用场景
- 给原有对象增加额外功能。业务系统的非功能性需求开发。例如监控、统计、鉴权、限流、事务以及日志等;
- 远程代理。RPC调用其他服务的方法时,生成的代理对象会执行一些网络通信,序列化和反序列化等非业务功能。