设计模式之(八):代理设计模式

代理模式章节简要

一.定义

为其他对象提供一种代理,以控制这个对象的访问

二.使用场景

  • 1.保护目标对象
  • 2.增强目标对象

三.优缺点

1.优点
  • 将代理对象与目与真实的被调用的目标对象分离
  • 一定程度上降低了系统的耦合度,扩展性好
  • 保护目标对象
    2.缺点
  • 造成系统设计中类的数目增加
  • 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
  • 增加了系统的复杂度

四.角色

  • 代理对象:类似于中介
  • 目标对象:被代理的对象
  • 客户端:调用端

五.分类

1.静态代理 : 直接指定代理类

静态代理的步骤:

  • 定义代理行为接口
  • 代理对象和目标对象都要实现代理行为接口
  • 代理对象直接调用目标对象的代理行为方法
2.动态代理:通过反射等动态指定代理类
(1) JDK的Proxy

步骤:

  • 定义代理行为接口
  • 目标对象实现代理行为接口
  • 定义代理行为处理器(要实现InvocationHandler接口,通过的反射的方式来调用目标对象的代理行为方法)
  • 通过Proxy.newProxyInstance来生成代理对象,使用代理对象调用代理行为方法

要求:

  • 要定义代理接口,才能调用接口实现类
(2)CGLIB:将目标对象指定代理对象的父类,来直接调用父类的代理行为接口

步骤

  • 定义目标对象
  • 定义代理拦截器,实现Callback接口
  • 实例化Enhancer对象
  • 设置enhancer对象的父类
  • 设置enhancer的回调对象
  • 创建代理对象
  • 调用目标对象的代理行为方法

要求

  • 目标类不能是final类,可以被继承

六、实例

通过一个租房的实例来说明,租房者即为目标对象,租房中介即为代理者(租房者通过租房中介租房,而不是直接跟房东租房)

1.静态代理实例
(1)租房要求实体
  1. /**
  2. * 租房要求
  3. * @author shixinke
  4. */
  5. public class Requirement {
  6. /**
  7. * 朝向
  8. */
  9. private String towards;
  10. /**
  11. * 面积
  12. */
  13. private Double area;
  14. /**
  15. * 价格
  16. */
  17. private Double price;
  18. /**
  19. * 离公交站或地铁站的距离
  20. */
  21. private Integer distance;
  22. public Requirement(String towards, Double area, Double price, Integer distance) {
  23. this.towards = towards;
  24. this.area = area;
  25. this.price = price;
  26. this.distance = distance;
  27. }
  28. public String getTowards() {
  29. return towards;
  30. }
  31. public Double getArea() {
  32. return area;
  33. }
  34. public Double getPrice() {
  35. return price;
  36. }
  37. public Integer getDistance() {
  38. return distance;
  39. }
  40. }
(2)租房接口(代理行为定义)
  1. /**
  2. * 出租接口
  3. * @author shixinke
  4. */
  5. public interface Rentable {
  6. /**
  7. * 租房子
  8. * @return boolean
  9. */
  10. boolean rent(Requirement requirement);
  11. }
(3)租房人(目标对象)实现租房的接口
  1. /**
  2. * 租房子的人
  3. * @author shixinke
  4. */
  5. public class Tenant implements Rentable {
  6. /**
  7. * 自己亲自找房源租房
  8. * @param requirement
  9. * @return
  10. */
  11. public boolean rent(Requirement requirement) {
  12. System.out.println("求租,要求:面积:"+requirement.getArea() + ";朝向:"+requirement.getTowards() +";距离公交或地铁站距离:"+requirement.getDistance() +";价格:"+requirement.getPrice());
  13. System.out.println("房子合适,决定签订合同");
  14. return true;
  15. }
  16. }
(4)代理对象实现代理行为接口
  1. /**
  2. * 租房中介
  3. * @author shixinke
  4. */
  5. public class RentProxy implements Rentable {
  6. /**
  7. * 目标用户
  8. */
  9. private Rentable target;
  10. public RentProxy(Rentable target) {
  11. this.target = target;
  12. }
  13. /**
  14. * 房产中介代理租房
  15. * @param requirement
  16. * @return
  17. */
  18. public boolean rent(Requirement requirement) {
  19. System.out.println("房产中介为您找到了匹配的结果:");
  20. //直接调用代理目标对象的代理行为方法
  21. target.rent(requirement);
  22. System.out.println("向房产中介交纳手续费,以及对房产中介进行评价");
  23. return true;
  24. }
  25. }

静态代理UML关系图

2.JDK动态代理的实例
(1)定义要求实体

与静态代理的定义相同,不再赘述

(2)定义代理行为接口

与静态代理的定义相同,不再赘述

(3)定义目标对象,实现代理行为接口

与静态代理的定义相同,不再赘述

(4)定义代理行为处理器,实现InvocationHandler接口
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. /**
  4. * 租房代理处理
  5. * @author shixinke
  6. */
  7. public class RentInvocationHandler implements InvocationHandler {
  8. private Rentable target;
  9. //将目标对象传入进来
  10. public RentInvocationHandler(Rentable target) {
  11. this.target = target;
  12. }
  13. /**
  14. *
  15. * @param proxy
  16. * @param method
  17. * @param args
  18. * @return
  19. * @throws Throwable
  20. */
  21. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  22. System.out.println("房产中介为您找到了匹配的结果:");
  23. //通过反射调用目标对象的代理方法
  24. Object result = method.invoke(target, args);
  25. System.out.println("向房产中介交纳手续费,以及对房产中介进行评价");
  26. return result;
  27. }
  28. }
(5)测试代码
  1. import java.lang.reflect.Proxy;
  2. /**
  3. * 动态代理测试
  4. * @author shixinke
  5. */
  6. public class DynamicProxyTest {
  7. public static void main(String[] args) {
  8. Rentable target = new Tenant();
  9. Requirement requirement = new Requirement(
  10. "朝南",
  11. 60D,
  12. 3000D,
  13. 800
  14. );
  15. //生成代理对象(要进行强转)
  16. Rentable proxy = (Rentable) Proxy.newProxyInstance(
  17. target.getClass().getClassLoader(),
  18. target.getClass().getInterfaces(),
  19. new RentInvocationHandler(target)
  20. );
  21. //调用代理对象的代理行为方法
  22. proxy.rent(requirement);
  23. }
  24. }

JDK动态代理UML关系图

3.CGLIB实现动态代理实例

maven依赖包:

  1. <dependency>
  2. <groupId>cglib</groupId>
  3. <artifactId>cglib</artifactId>
  4. <version>3.3.0</version>
  5. </dependency>
(1)定义要求实体

与静态代理的定义相同,不再赘述

(2)定义目标对象,实现代理行为接口

与静态代理的定义相同,不再赘述

(3)定义代理拦截器,实现Callback接口
  1. import net.sf.cglib.proxy.MethodInterceptor;
  2. import net.sf.cglib.proxy.MethodProxy;
  3. import java.lang.reflect.Method;
  4. /**
  5. * 租房拦截器
  6. */
  7. public class RentInterceptor implements MethodInterceptor {
  8. /**
  9. * 拦截操作
  10. * @param o
  11. * @param method
  12. * @param objects
  13. * @param methodProxy
  14. * @return
  15. * @throws Throwable
  16. */
  17. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  18. System.out.println("房产中介为您找到了匹配的结果:");
  19. Object result = methodProxy.invokeSuper(o, objects);
  20. System.out.println("向房产中介交纳手续费,以及对房产中介进行评价");
  21. return result;
  22. }
  23. }
(4)测试代码
  1. import net.sf.cglib.proxy.Enhancer;
  2. /**
  3. * 通过Cglib实现动态代理
  4. * @author shixinke
  5. */
  6. public class CglibProxyTest {
  7. public static void main(String[] args) {
  8. Requirement requirement = new Requirement(
  9. "朝南",
  10. 60D,
  11. 3000D,
  12. 800
  13. );
  14. //实例化Enhancer对象
  15. Enhancer enhancer = new Enhancer();
  16. // 设置enhancer对象的父类
  17. enhancer.setSuperclass(Tenant.class);
  18. // 设置enhancer的回调对象
  19. enhancer.setCallback(new RentInterceptor());
  20. // 创建代理对象
  21. Tenant proxy= (Tenant) enhancer.create();
  22. proxy.rent(requirement);
  23. }
  24. }

CGLIB实现动态代理的UML关系图