Java - GoF设计模式详解12(代理模式)
作者:hangge | 2023-05-05 09:52
十二、代理模式
1,基本介绍
(1)代理模式(Proxy)是为目标对象提供一种代理,从而能够在不改变目标对象的情况下,对目标对象的访问进行控制。例如:访问权限的控制、访问地址的控制、访问方式的控制等。
(2)该模式中包含的角色及其职责如下:
- 抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
2,使用样例
(1)下面我们通过样例演示如何使用代理模式在 Java 中实现缓存代理。首先定义一个抽象角色 DataService,声明需要实现的获取数据方法:
// 数据服务接口(抽象角色) public interface DataService { String getData(String key); }
(2)接着定义一个真实角色 RealDataService,就是正常从数据库或者其他数据源获取数据:
//真实数据服务(真实角色) public class RealDataService implements DataService { @Override public String getData(String key) { // 从数据库或其他数据源获取数据 System.out.println("--- 从数据库获取数据 ---"); return "这是" + key + "的数据"; } }
(3)然后定义一个代理角色 CacheDataService,它充当了客户端和 RealDataService 类之间的中介。CacheDataService 会在调用 RealDataService 类的 getData() 方法之前,先检查是否已经缓存了对应的数据,如果已经缓存了,就直接返回缓存的数据;如果没有缓存,则会创建 RealDataService 对象并调用 getData() 方法,然后将获取的数据缓存起来。这样就能实现缓存代理的目的,即在调用目标对象之前,先检查是否有缓存数据,如果有,就直接返回缓存的数据,避免不必要的访问目标对象。
// 缓存数据服务(代理角色) public class CacheDataService implements DataService { //RealDataService对象引用 private RealDataService realDataService; //缓存数据 private Map<String, String> cache; public CacheDataService() { cache = new HashMap<>(); } @Override public String getData(String key) { // 检查是否已经缓存了对应的数据 if (cache.containsKey(key)) { // 如果已经缓存了,就直接返回缓存的数据 System.out.println("--- 从缓存获取数据 ---"); return cache.get(key); } // 延迟加载 RealDataService 对象 if (realDataService == null) { realDataService = new RealDataService(); } // 使用 RealDataService 对象获取真实数据 String data = realDataService.getData(key); // 将获取的数据缓存起来 cache.put(key, data); // 返回获取的数据 return data; } }
(4)最后测试一下:
public class Test { @SneakyThrows public static void main(String[] args) { DataService service = new CacheDataService(); System.out.println(service.getData("key1")); System.out.println(service.getData("key2")); System.out.println(service.getData("key1")); } }
附一:JDK 中的代理模式
(1)在 JDK 中,Java 的动态代理是使用代理模式实现的。动态代理是指在运行时动态生成代理类的代理,通常用于在不修改目标类的情况下,对目标类的方法进行增强。在 JDK 中,java.lang.reflect 包中的 Proxy 类提供了用于创建动态代理类和实例的方法。使用动态代理需要提供一个接口和一个 InvocationHandler 实例,Proxy 类会根据这些信息生成动态代理类。
关于代理更详细的介绍可以参考我写的另一篇文章:Java - 静态代理与动态代理详解(附:Cglib的使用)
(2)JDK 中还有很多其他的例子。例如,Java 的远程方法调用(Remote Method Invocation,简称 RMI)也是使用代理模式实现的。
附二:Spring 中的代理模式
(1)在 Spring 中,AOP(面向切面编程 Aspect-Oriented Programming)是通过代理机制实现的,也就是说,Spring 会在运行时为目标对象创建一个代理对象,并将切面逻辑织入到代理对象中。
(2)当我们在使用 Spring AOP 时,我们所编写的切面代码实际上是通过一个特殊的对象——"切面"(Aspect)来表示的。切面是由一个或多个切入点(Pointcut)和通知(Advice)组成的。切入点表示在哪些方法上应用通知,而通知则表示在什么时候应用切面逻辑。
- 基于接口的动态代理要求目标对象实现一个或多个接口,并且会为目标对象创建一个动态代理类,该代理类实现了与目标对象实现的接口相同的接口。
- 基于类的动态代理则不要求目标对象实现接口,而是通过反射机制在运行时创建一个代理类,该代理类继承了目标类,并在代理类中应用切面逻辑。
总结:如果加入容器的目标对象有实现接口,Spring AOP 则用动态代理;如果目标对象没有实现接口,则用 Cglib 代理。
全部评论(0)