【读书笔记】大话设计模式—代理模式

代理模式(使用频率:4颗星)

      代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

Proxy Pattern: Provide a surrogate or placeholder for another object to control access to it.

       代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。
常见的代理形式包括远程代理、保护代理、虚拟代理、缓冲代理、智能引用代理等

      代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层,代理模式结构如图15-2所示:

【读书笔记】大话设计模式—代理模式_第1张图片

15-2 代理模式结构图

       由图15-2可知,代理模式包含如下三个角色:

       (1) Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。

       (2) Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。

       (3) RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

常用代理模式:静态代理+动态代理

代理模式又称作委托模式,许多其他模式(例如访问者模式、策略模式、状态模式)本质上实在更特殊的场合采用了代理模式

代理模式分为静态代理动态代理

(1)静态代理

所谓静态,就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了

静态代理类的优点是,业务类只需要关注业务逻辑本身,保证了业务类的重用性

  1. //委托类和代理类共同的接口  
  2. public interfaceMoveable{  
  3.       void move();  
  4. }  
  5.    
  6. //记录坦克运行时间的代理类  
  7. public class TanktimeProxy impelments Moveable {  
  8.       private Moveable t;  //代理类持有委托类的对象  
  9.       public TanktimeProxy(Moveable t){  
  10.              super();  
  11.              this.t = t;  
  12.       }  
  13.       public void move(){  
  14.              longtime1 = System.currentTimeMillis();  
  15.              t.move();  
  16.              long time2 =System.currentTimeMillis();  
  17.              System.outl.println("坦克运行时间:"+(time2-time1));  
  18.       }  
  19. }  
  20.    
  21. //记录坦克运行日志的代理类  
  22. public class TanklogProxy impelments Moveable {  
  23.       private Moveable t;  //代理类持有委托类的对象  
  24.       public TanklogProxy (Moveable t){  
  25.              super();  
  26.              this.t = t;  
  27.       }  
  28.       public void move(){  
  29.              System.out.println("坦克开始移动");  
  30.              t.move();  
  31.              long time2 =System.currentTimeMillis();  
  32.              System.out.println("坦克停止移动");  
  33.       }  
  34. }  
  35. //委托类  
  36. public class Tank implements Moveable{  
  37.       public void move(){  
  38.              System.out.println("坦克移动……");  
  39.       }  
  40. }  
  41.    
  42. //测试类  
  43. Tank t = newTank();  
  44. Moveable move1 =new TanktimeProxy(t);  
  45. Moveable move2 =new TanklogProxy (move1);  
  46. move2.move();   

这样就通过代理在Tank的move方法前后加入了日志和时间统计功能

(2)动态代理

与动态代理相关的Java API:

java.lang.reflect.Proxy:这是java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来作为一组接口动态的生成代理类及其对象

//该方法用于获取指定代理对象所关联的调用处理器

staticInvocationHandler getInvocationHandler(Objectproxy)

//该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

static Class getProxyClass(ClassLoader loader,Class[] interfaces)

//该方法用于判断指定类对象是否是一个动态代理类

static boolean isProxyClass(Class cl)

//该方法用于为指定类装载器、一组接口和调用处理器生成动态代理类实例

static Object newProxyInstance(ClassLoader loader,Class [] interfaces, InvocationHandler h)

重点:

java.lang.reflect.InvocationHandler:这是调用处理器接口,它定义了一个invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问

//第一个参数是代理实例,第二个参数是被调用的方法,第三个参数是调用方法时用到的参数

//调用处理器根据这三个参数进行预处理或分派到委托实例上反射执行

Object invoke(Object proxy, Method method,Object [] args)

java.lang.reflect.ClassLoader:类装载器,负责将类的字节码装载到java虚拟机中并为其定义类对象,然后该类才能被调用

动态代理其实就是java.lang.reflect.Proxy类动态的根据指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(在参数中传入的接口数组);然后再利用指定的classloader将 class byte加载进系统,最后生成这样一个类的对象,并初始化该对象的一些值,如invocationHandler,以即所有的接口对应的Method成员。初始化之后将对象返回给调用的客户端。这样客户端拿到的就是一个实现你所有的接口的Proxy对象。请看实例分析:

  1. //业务接口类  
  2. public interface BusinessProcessor {  
  3.  public void processBusiness();  
  4. }    
  5.   //业务实现类  
  6. public class BusinessProcessorImpl implements BusinessProcessor {  
  7.  public void processBusiness() {  
  8.   System.out.println("processingbusiness.....");  
  9.  }  
  10. }  
  11.    
  12. //业务代理类  
  13. import java.lang.reflect.InvocationHandler;  
  14. import java.lang.reflect.Method;   
  15. public class BusinessProcessorHandler implements InvocationHandler{  
  16.  private Object target = null;   
  17.  BusinessProcessorHandler(Object target){  
  18.   this.target = target;  
  19.  }  
  20.    
  21.  public Object invoke(Object proxy, Methodmethod, Object[] args)  
  22.    throws Throwable {  
  23.   System.out.println("You can dosomething here before process your business");  
  24.   Object result = method.invoke(target, args);  
  25.   System.out.println("You can dosomething here after process your business");  
  26.   return result;  
  27.  }  
  28.    
  29. }  
  30.    
  31. //客户端应用类  
  32. import java.lang.reflect.Field;  
  33. import java.lang.reflect.Method;  
  34. import java.lang.reflect.Modifier;  
  35. import java.lang.reflect.Proxy;   
  36. public class Test {   
  37.  public static void main(String[] args) {  
  38.   BusinessProcessorImpl bpimpl = newBusinessProcessorImpl();  
  39.   BusinessProcessorHandler handler = newBusinessProcessorHandler(bpimpl);  
  40.   BusinessProcessor bp =(BusinessProcessor)Proxy.newProxyInstance(bpimpl.getClass().getClassLoader(),bpimpl.getClass().getInterfaces(), handler);  
  41.   bp.processBusiness();  
  42.  }  
  43. }  

打印结果:

You can dosomething here before process your business

processingbusiness.....

You can dosomething here after process your business

通过结果我们就能够很简单的看出Proxy的作用了,它能够在你的核心业务方法前后做一些你所想做的辅助工作,如log日志,安全机制等等。


Proxy.newProxyInstance方法会做如下几件事:

1,根据传入的第二个参数interfaces动态生成一个类,实现interfaces中的接口,该例中即BusinessProcessor接口的processBusiness方法。并且继承了Proxy类,重写了hashcode,toString,equals等三个方法。

2,通过传入的第一个参数classloder将刚生成的类加载到jvm中。即将$Proxy0类load

3,利用第三个参数,调用$Proxy0的$Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象,并且用interfaces参数遍历其所有接口的方法,并生成Method对象初始化对象的几个Method成员变量

4,将$Proxy0的实例返回给客户端。


再看客户端怎么调

1,客户端拿到的是$Proxy0的实例对象,由于$Proxy0继承了BusinessProcessor,因此转化为BusinessProcessor没任何问题。

BusinessProcessorbp = (BusinessProcessor)Proxy.newProxyInstance(....);

2,bp.processBusiness();

实际上调用的是$Proxy0.processBusiness();那么$Proxy0.processBusiness()的实现就是通过InvocationHandler去调用invoke方法

你可能感兴趣的:(设计模式)