返回 导航

其他

hangge.com

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)也是使用代理模式实现的。
关于 RMI 更详细的介绍,可以参考我之前写的文章:
 

附二:Spring 中的代理模式

(1)在 Spring 中,AOP(面向切面编程 Aspect-Oriented Programming)是通过代理机制实现的,也就是说,Spring 会在运行时为目标对象创建一个代理对象,并将切面逻辑织入到代理对象中。

(2)当我们在使用 Spring AOP 时,我们所编写的切面代码实际上是通过一个特殊的对象——"切面"(Aspect)来表示的。切面是由一个或多个切入点(Pointcut)和通知(Advice)组成的。切入点表示在哪些方法上应用通知,而通知则表示在什么时候应用切面逻辑。
关于 Spring AOP 更详细的用法,可以参考我之前写的文章:

(3)Spring AOP 可以使用基于接口的动态代理或基于类的动态代理来实现代理对象:
总结:如果加入容器的目标对象有实现接口,Spring AOP 则用动态代理;如果目标对象没有实现接口,则用 Cglib 代理。
评论

全部评论(0)

回到顶部