代理设计模式
-
简单介绍
代理设计模式是一种结构型设计模式。该模式是将能在不修改既有代码的前提下,完成对被代理对象的功能扩展,以及安全控制等功能。
-
使用场景
- 远程代理:目标类存在于另一个地址空间(可以简单理解为在另一台服务器上),在调用目标类接口方法时,可以通过调用代理类的接口方法,再由代理类与远端目标类进行通信。完成远程方法的透明调用。RPC框架的实现原理便是如此
- 保护代理:目标对象的调用需要进行安全控制时,此时安全控制可交由代理对象,由代理对象过滤不安全的因素
- 虚拟代理:将创建开销大的对象,交由创建开销小的代理对象进行代理。将大对象的创建延迟到真正调用它的时候才去创建
-
场景举例
Spring框架的核心功能AOP底层采用的便是动态代理模式。通过用户的配置,在不改变既有接口代码的前提下,完成对目标方法的增强。
-
UML类图
-
实现方式
-
静态代理
代理类(Proxy)由用户自己创建,在编译期确定代理对象,即在程序运行前便生成代理类的字节码文件
-
JDK动态代理
通过JDK自带的Proxy.newProxyInstance()方法,在运行期间生成目标类的代理对象。与静态代理相似的是,目标类需要实现接口
-
cglib动态代理
目标类不需要实现接口,cglib思想是通过继承目标类,获取目标对象的元信息,故目标类不能为final类,被代理的方法不能为final方法,因为final类型的类或方法不能被子类所继承
-
-
具体实现
-
描述
- 背景:为了保证明星(被代理对象)的人身安全,当高空高度大于1米时,替身(代理对象)代理大碗明星完成部分高危动作高空跳跃
- Jumpable:公共接口(Subject),拥有高空跳跃的接口方法,该方法需要被代理执行
- SuperStar:明星(被代理对象,RealSubject)。当高空高度较高时,跳跃动作交由替身(被代理对象)完成
- Substitute:替身(代理对象,Proxy)。当高空高度较高时,由替身(被代理对象)完成该场景下的代理模式,起到了保护明星(被代理对象)的作用
-
静态代理
Jumpable.java
/** * 角色:Subject * 代理与被代理对象共同接口 * 接口方法为高空跳跃,即替身(代理类)需要代理明星(被代理类)完成的动作(方法) */ public interface Jumpable { /** * 高空跳下 */ void highJump(); }
SuperStar.java
/** * 角色:RealSubject(目标类) * 明星,即目标类 */ public class SuperStar implements Jumpable { @Override public void highJump() { System.out.println("明星完成了高空跳跃动作"); } }
Substitute.java
/** * 角色:Proxy * 替身,即代理对象 * 高空较高时,跳跃由替身(代理对象)完成 * 起到了保护明星(目标对象)的作用 */ public class Substitute implements Jumpable { /** * 被代理对象(明星) */ private SuperStar superStar; /** * 高空跳跃的高度 */ private double highAltitude; /** * 通过构造方法,将被代理对象聚合到代理对象中 */ public Substitute(SuperStar superStar, double highAltitude) { this.superStar = superStar; this.highAltitude = highAltitude; } /** * 高空跳跃 * 为了保护明星,当高空的高度大于1米,则跳跃动作有替身完成 * 如果高空的高度小于等于1米,则跳跃动作有替身完成 */ @Override public void highJump() { // 高空高度小于1米 if (highAltitude <= 1) { System.out.print(String.format("高空为:%s米,", highAltitude)); superStar.highJump(); } else { System.out.print(String.format("高空为:%s米,", highAltitude)); System.out.println("为保证明星安全,替身完成了高空跳跃动作"); } } }
Client.java
/** * 客户类 * 可以将该客户类理解为导演 */ public class Client { public static void main(String[] args) { // 当高空高度为0.8米时,代理对象将高空跳跃动作交由明星(目标对象)完成 Jumpable proxy01 = new Substitute(new SuperStar(), 0.8D); proxy01.highJump(); // 当高空高度为1.8米时,为保证明星(目标对象)人身安全,高空跳跃动作由替身(代理对象)完成 Jumpable proxy02 = new Substitute(new SuperStar(), 1.8D); proxy02.highJump(); } }
-
JDK动态代理
PowerSupply.java
/** * 角色:Subject * 代理与被代理对象共同接口 * 接口方法为高空跳跃,即替身(代理类)需要代理明星(被代理类)完成的动作(方法) */ public interface Jumpable { /** * 高空跳下 */ void highJump(); }
SuperStar.java
/** * 角色:RealSubject(被代理类) * */ public class SuperStar implements Jumpable { @Override public void highJump() { System.out.println("明星完成了高空跳跃动作"); } }
Client.java
/** * 客户类 * 可以将该客户类理解为导演 */ public class Client { public static void main(String[] args) { Jumpable superStar = new SuperStar(); // 手动输入高空高度 double highAltitude = new Scanner(System.in).nextDouble(); // 通过JDK动态代理,生成明星(目标对象)的代理对象替身substitute Jumpable substitute = (Jumpable) Proxy.newProxyInstance(superStar.getClass().getClassLoader(), superStar.getClass().getInterfaces(), (proxy, method, args1) -> { // 高空高度小于1米 if (highAltitude <= 1) { System.out.printf("高空为:%.2f米,", highAltitude); method.invoke(superStar, args1); } else { System.out.print(String.format("高空为:%.2f米,", highAltitude)); System.out.println("为保证明星安全,替身完成了高空跳跃动作"); } return null; }); // 调用代理对象的highJump()方法 // 输入1.5,输出 —— 高空为:1.50米,为保证明星安全,替身完成了高空跳跃动作 // 输入0.8,输出 —— 高空为:0.80米,明星完成了高空跳跃动作 substitute.highJump(); } }
-
cglib动态代理
SuperStar.java
/** * 角色:RealSubject(目标类) * 明星,即目标类 */ public class SuperStar implements Jumpable { @Override public void highJump() { System.out.println("明星完成了高空跳跃动作"); } }
ProxyFactory.java
/** * 代理类工厂,用于创建目标对象代理类, * 需要实现MethodInterceptor接口,用于拦截目标类的方法 */ public class ProxyFactory implements MethodInterceptor { /** * 被代理对象(明星) */ private SuperStar superStar; /** * 高空跳跃的高度 */ private double highAltitude; /** * 通过构造方法,将被代理对象聚合到代理对象中 */ public ProxyFactory(SuperStar superStar, double highAltitude) { this.superStar = superStar; this.highAltitude = highAltitude; } /** * 目标对象方法执行时,被拦截进入该方法中 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // 高空高度小于1米 if (highAltitude <= 1) { System.out.print(String.format("高空为:%s米,", highAltitude)); superStar.highJump(); } else { System.out.print(String.format("高空为:%s米,", highAltitude)); System.out.println("为保证明星安全,替身完成了高空跳跃动作"); } // 无返回值时,返回null即可 return null; } /** * 获取代理对象 */ public SuperStar getProxyInstance() { // 工具类创建 Enhancer enhancer = new Enhancer(); // 设备目标对象为代理对象父类 enhancer.setSuperclass(superStar.getClass()); // 设置回调对象实例 // ProxyFactory实现了MethodInterceptor接口,故设置本实例即可 enhancer.setCallback(this); // 创建并返回代理对象 return (SuperStar) enhancer.create(); } }
Client.java
/** * 客户类 * 可以将该客户类理解为导演 */ public class Client { public static void main(String[] args) { // 1.1 创建目标类 SuperStar superStar = new SuperStar(); // 1.2 创建代理对象1,设置高空高度为1.8m SuperStar substitute1 = new ProxyFactory(superStar, 1.8D).getProxyInstance(); // 1.2 跳跃动作交由代理类(替身) // 输出:高空为:1.8米,为保证明星安全,替身完成了高空跳跃动作 substitute1.highJump(); // 2.1 创建代理对象2,设置高空高度为0.8m SuperStar substitute2 = new ProxyFactory(superStar, 0.8D).getProxyInstance(); // 2.2 跳跃动作交由代理类(替身) // 输出:高空为:0.8米,明星完成了高空跳跃动作 substitute2.highJump(); } }
-
-
源码展示
-
MaBatis中的SQL执行器Executor,采用了代理模式,其中接口类为 Executor,目标类为 BaseExecutor 的具体子类( BaseExecutor、SimpleExecutor等),代理类为CachingExecutor。CachingExecutor作为代理类,采用缓存的方式,来提升目标类的查询效率。当内存中存在SQL执行的结果时,则直接将内存中的数据返回,否则调用具体的目标类,执行相关SQL。
Executor.java
public interface Executor { /** * 查询操作 */ <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException; // 剩余代码略 }
BaseExecutor.java
public abstract class BaseExecutor implements Executor { /** * 目标类实现的查询操作 */ public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (this.closed) { throw new ExecutorException("Executor was closed."); } else { if (this.queryStack == 0 && ms.isFlushCacheRequired()) { this.clearLocalCache(); } List list; try { ++this.queryStack; list = resultHandler == null ? (List)this.localCache.getObject(key) : null; if (list != null) { this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { --this.queryStack; } if (this.queryStack == 0) { Iterator i$ = this.deferredLoads.iterator(); while(i$.hasNext()) { BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next(); deferredLoad.load(); } this.deferredLoads.clear(); if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { this.clearLocalCache(); } } return list; } } }
CachingExecutor.java
public class CachingExecutor implements Executor { /** * 持有目标类引用 */ private Executor delegate; /** * 通过构造函数,聚合目标类 */ public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } /** * 对目标类的query方法进行增强 */ public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 先查缓存,缓存中有结果则直接返回 Cache cache = ms.getCache(); if (cache != null) { this.flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { this.ensureNoOutParams(ms, parameterObject, boundSql); List<E> list = (List)this.tcm.getObject(cache, key); if (list == null) { list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); this.tcm.putObject(cache, key, list); } return list; } } // 缓存中没有结果,则调用目标类的query()方法 return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } }
-