Ref: https://javadoop.com/post/design-pattern#toc_9

代理模式

第一个要介绍的代理模式是最常使用的模式之一了,用一个代理来隐藏具体实现类的实现细节,通常还用于在真实的实现的前后添加一部分逻辑。
既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,而是一层皮而已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。

理解代理这个词,这个模式其实就简单了。

  1. public interface FoodService {
  2. Food makeChicken();
  3. Food makeNoodle();
  4. }
  5. public class FoodServiceImpl implements FoodService {
  6. public Food makeChicken() {
  7. Food f = new Chicken()
  8. f.setChicken("1kg");
  9. f.setSpicy("1g");
  10. f.setSalt("3g");
  11. return f;
  12. }
  13. public Food makeNoodle() {
  14. Food f = new Noodle();
  15. f.setNoodle("500g");
  16. f.setSalt("5g");
  17. return f;
  18. }
  19. }
  20. // 代理要表现得“就像是”真实实现类,所以需要实现 FoodService
  21. public class FoodServiceProxy implements FoodService {
  22. // 内部一定要有一个真实的实现类,当然也可以通过构造方法注入
  23. private FoodService foodService = new FoodServiceImpl();
  24. public Food makeChicken() {
  25. System.out.println("我们马上要开始制作鸡肉了");
  26. // 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的,
  27. // 代理只是在核心代码前后做些“无足轻重”的事情
  28. Food food = foodService.makeChicken();
  29. System.out.println("鸡肉制作完成啦,加点胡椒粉"); // 增强
  30. food.addCondiment("pepper");
  31. return food;
  32. }
  33. public Food makeNoodle() {
  34. System.out.println("准备制作拉面~");
  35. Food food = foodService.makeNoodle();
  36. System.out.println("制作完成啦")
  37. return food;
  38. }
  39. }

客户端调用,注意,我们要用代理来实例化接口:

  1. // 这里用代理类来实例化
  2. FoodService foodService = new FoodServiceProxy();
  3. foodService.makeChicken();

image.png
我们发现没有,代理模式说白了就是做 “方法包装” 或做 “方法增强”。在面向切面编程中,其实就是动态代理的过程。比如 Spring 中,我们自己不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在 @Before@After@Around 中的代码逻辑动态添加到代理中。

说到动态代理,又可以展开说,Spring 中实现动态代理有两种,一种是如果我们的类定义了接口,如 UserService 接口和 UserServiceImpl 实现,那么采用 JDK 的动态代理,感兴趣的读者可以去看看 java.lang.reflect.Proxy 类的源码;另一种是我们自己没有定义接口的,Spring 会采用 CGLIB 进行动态代理,它是一个 jar 包,性能还不错。