介绍
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式种,通过创建具有现有对象的对象,以便于向外界提供功能接口。
意图:为其他对象提供一种代理以控制对这个对象的访问
主要解决:直接访问对象带来的问题,在某些情况下(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),用户直接访问对象会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
如何解决:增加中间层
关键代码:实现与被代理类组合。
优点:
- 职责清晰。
- 高扩展性。
- 智能化。
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
目前,代理主要有这样几种方式进行:
- 静态代理
- 动态代理
- cglib代理
静态代理
- 这种代理方式需要代理对象和目标对象实现一样的接口。
- 就是在代理对象中生成目标对象,并且在需要代理的方法中,直接调用目标对象的方法就可以了
- 优点:可以在不修改目标对象的前提下扩展目标对象的功能
- 缺点:
- 冗余;由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
- 不易维护;一旦接口增加方法,目标对象与代理对象都要进行修改
静态代理示例
现有一个订单操作接口,有一个实现类,现在需要扩展实现类的功能,但是不能让用户接触实现类对象,从而使用了代理模式,新建一个实现类,新实现类内部使用原有实现类的方法即可。
UML类图
程序实例
操作接口:
1 | public interface IOrderService { |
实现类:
1 | public class OrderServiceImpl implements IOrderService{ |
代理类:
1 | public class OrderServiceStaticProxy implements IOrderService { |
测试过程:
1 | public class Test { |
从上述示例可以看到,通过静态代理模式,在不改变原有实现类的基础上,通过代理类增强了原有类的方法。
动态代理
首先要知道的是,动态代理无法代理类,只能代理接口。
- 动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
- 动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理
java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心
InvocationHandler接口
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口。
每一个proxy代理实例都有一个关联的调用处理程序,每个调用处理程序都实现了InvocationHandler接口;
在代理实例调用方法时,方法调用在编码后,分派到调用处理程序的invoke方法。
官方文档对InvocationHandler接口的描述:
{@code InvocationHandler} is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the {@code invoke} method of its invocation handler.
我们能够发现,在动态代理中,代理对象调用方法时,是通过InvocationHandler接口的invoke方法实现。
1 | /** |
Proxy类
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
1 | public static Object newProxyInstance(ClassLoader loader, |
此方法的作用就是创建一个指定类的代理对象,有三个参数:
- ClassLoader loader:一个classloader对象,定义了由哪个类加载器对生成的代理类进行加载
- 在jvm中,由不同类加载加载同一个类而生成的Class对象并不相同
- Class<?>[] interfaces:一个interface对象数组,里面是指定类所实现的接口,同样,代理对象也需要声明实现了这些接口,才能调用接口中声明的方法
- InvocationHandler h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到这一个InvocationHandler对象上,并最终由其调用。(InvocationHandler对象通过invoke方法调用方法,上面已经提到了)
动态代理示例
定义一个接口类,一个实现类,通过动态代理的方式去使用接口的实现类的方法
- 首先,定义一个接口People
1 | public interface People { |
- 定义一个Teacher类,实现了People对象
1 | public class Teacher implements People{ |
- 定义代理对象的方法处理程序,也就是定义一个InvocationHandler接口的实现类
1 | public class WorkHandler implements InvocationHandler{ |
- 开始创建代理类,并使用
1 | //第一种方式 |
cglib代理
运行时在内存中 动态生成一个子类对象,从而实现对目标对象功能的扩展。
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
- CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
cglib与动态代理的区别:
- 使用动态代理的对象必须实现一个或多个接口
- 使用cglib代理的对象则无需实现接口,达到代理类无侵入。
参考
- 本文作者: xczll
- 本文链接: https://xczllgit.github.io/2020/03/20/designPattern/2020-03-20-proyPattern/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!