设计模式:代理模式
代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
介绍
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:Spring-AOP
注意事项:
- 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
- 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
实现
静态模式
动态模式
作用: 与静态代理一样,但动态代理解决了 静态代理中 1个静态代理,只代理1种类型的目标对象 的问题
1. JDK动态代理
在 JDK 动态代理中涉及如下角色:
- 目标类的接口 Interface
- 目标类 target
- 处理模版 Handler
- 在内存中生成的动态代理类
- java.lang.reflect.Proxy
动态代理类实现了目标类的接口,并实现了接口中的方法,该方法的实现逻辑是, 调用父类 —— Proxy 的 h 的 invoke()方法
其中h 是 在创建动态代理实例时 newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) 传入的第3个参数InvocationHandler对象
在 InvocationHandler.invoke()中通过反射,调用目标对象。
特点: 不需要显式实现与目标类相同的接口,而是将这种实现推迟到程序调用时由 JVM来实现,
- 即:在使用时再创建动态代理类 和 实例
- 静态代理则是在代理类实现时就指定与目标相同的接口
优点:一个代理类就可以代理多个目标类,避免重复、多余代码
缺点
- 相比与静态代理,效率低。 静态代理是直接调用目标对象方法,而动态代理则需要先生成类和对象,在通过Java反射机制间接调用目标对象的方法
- 应用场景局限, 由于每个代理类都继承了 java.lang.reflect.Proxy 类,而Java又只能支持单继承,导致不能针对类 创建代理类,只能针对接口 创建 代理类。即,动态代理只能代理实现了接口的类。
应用场景
- 需要代理的对象数量较多的情况下。数量较少时,推荐直接使用静态代理
- 面向切面编程时
2. cglib动态代理
代理类必须要和目标类是同一个类型!
这里的“同一个类型”指的是
- 要么,代理类和目标类都实现了同一个接口,他们都属于同一种类型,即都是该接口的子类。
- 要么,代理类继承自目标类,这也是属于同一种类型,前提是目标类没有被 final 关键字修饰
3. CGLib的动态代理 与java动态代理的区别
- Java动态代理生成的代理类和目标类是 “兄弟”关系
- cglib动态代理生成的代理类和目标类是 “父子”关系
局限性
- Java动态代理依赖于目标类有实现接口
- cglib动态代依赖于目标类没有被final关键字修饰