代理模式是一种非常好理解的设计模式,生活中处处可见代理:张三作为一个明星,不可能什么事都由他自己干,于是他请了经纪人;张三 LOL 水平不够,又想上分怎么办,请游戏代练;张三逃税被抓怎么办,请律师帮忙打官司。无论是经纪人、游戏代练或是律师,他们都是帮张三干活,但又不能一手包办,只能帮张三处理一些他不愿干或干不了的事情。
代理模式
代理模式(Proxy Pattern)是通过代理对象来访问目标对象,可以在实现目标对象功能的基础上,增加额外的操作,达到扩展目标对象功能的目的。
类结构图
静态代理
定义
静态代理是定义接口或者父类,然后被代理对象(RealSubject)与代理对象(Proxy)一起实现相同的接口或继承相同的父类。
示例
1 | public interface Subject { |
1 | public class RealSubject implements Subject{ |
1 | public class Proxy implements Subject { |
1 | public class Client { |
运行结果
JDK 动态代理
定义
JDK 代理的目标对象必须要实现接口,而代理对象则是在内存中动态生成的。
JDK 代理使用 java.lang.reflect.Proxy 中的 newProxyInstance 方法来创建代理对象,该方法需要如下三个参数:
- ClassLoader loader
- 代理类的类加载器
- Class<?>[] interfaces
- 代理类要实现的接口列表
- InvocationHandler h
- 执行代理方法的处理器
JDK 动态代理类图
示例
1 | public class JDKProxy { |
1 | public class Client { |
运行结果
cglib 动态代理
定义
cglib(Code Generation Library) 代理的目标对象不需要实现接口,它是在内存中构建一个子类对象,从而实现对目标对象的代理。
cglib 是一个高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口。被许多的 AOP 框架使用(如 Spring AOP)。cglib 包的底层是使用字节码处理框架 ASM 来生成新的类。
cglib 代理的类不能为 final,否则会报 java.lang.IllegalArgumentException 异常,这是因为 final 修饰的类不能被继承。 如果目标对象的方法为 final 或 static,那么也不会被拦截(只会执行目标对象的方法,不会执行代理逻辑)。
cglib 动态代理类图
代理对象需要实现 MethodInterceptor 接口中的 intercept() 方法。
示例
将 jar 包添加到项目中
1 | public class CglibProxy implements MethodInterceptor { |
1 | public class Client { |
运行结果
小结
静态代理
- 优点:简单。
- 缺点:每一个目标对象都需要一个代理对象,所以会产生很多的代理类。一旦接口中新增方法,目标对象与代理对象都需要维护,十分繁琐。
JDK 动态代理
- 优点:不需要为每一个目标对象写一个代理类。
- 缺点:目标对象必须要实现接口才能代理。
cglib 动态代理
- 优点:因为是基于继承来实现的代理,目标对象没有实现接口也能代理。
- 缺点:不能代理 final 修饰的类和方法,因为 final 修饰的类不能被继承。
拓展
为什么 JDK 动态代理只能代理实现了接口的类?
因为 JDK 生成的代理类已经继承了 java.lang.reflect.Proxy 类,而 Java 只支持单继承,所以只能通过接口来实现代理。
改造 JDKProxy 类
1 | public class JDKProxy { |
运行结果
JDKProxy.class
查看 cglib 生成的代理类
改造 CglibProxy 类
1 | public class CglibProxy implements MethodInterceptor { |
改造 Client 类
1 | public class Client { |
运行结果
CglibProxy.class