zoukankan      html  css  js  c++  java
  • 【java代理】动态代理

    java代理机制

    1 引言

    我们书写执行一个功能的函数时,经常需要在其中写入与功能不是直接相关但很有必要的代 码,如日志记录,信息发送,安全和事务支持等,这些枝节性代码虽然是必要的,但它会带 来以下麻烦:

    1. 枝节性代码游离在功能性代码之外,它下是函数的目的,这是对OO是一种破坏
    2. 枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合,而这是OO系统所竭 力避免的
    3. 枝节性代码带来的耦合度会造成功能性代码移植困难,可重用性降低
    4. 从法理上说,枝节性代码应该`监视'着功能性代码,然后采取行动,而不是功能性代码 `通知'枝节性代码采取行动,这好比吟游诗人应该是主动记录骑士的功绩而不是骑士主 动要求诗人记录自己的功绩

    2 常见的代理

    毫无疑问,枝节性代码和功能性代码需要分开来才能降低耦合程度,符合现代OO系统的要 求,我们可以使用代理模式完成这个要求。

    代理模式的作用是:为其它对象提供一种代理以控制对这个对象的访问。在某些情况下,一 个客户不想直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。 代理模式一般涉及到三个角色:

    1. 抽象角色:声明真实对象和代理对象的共同接口
    2. 代理角色:代理对象内部包含有真实角色的引用,从而可以操作真实角色,同时代理对象 与真实对象有相同的接口,能在任何时候代替真实对象,同时代理对象可以在执行真实对 象前后加入特定的逻辑以实现功能的扩展。
    3. 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

    常见的代理有:

    1. 远程代理(Remote Proxy):对一个位于不同的地址空间对象提供一个局域代表对象,如RMI中的stub
    2. 虚拟代理(Virtual Proxy):根据需要将一个资源消耗很大或者比较复杂的对象,延迟加 载,在真正需要的时候才创建
    3. 保护代理(Protect or Access Proxy):控制对一个对象的访问权限。
    4. 智能引用(Smart Reference Proxy):提供比目标对象额外的服务和功能。

    通过代理类这一中间层,能够有效控制对实际委托类对象的直接访问,也可以很好地隐藏和 保护实际对象,实施不同的控制策略,从而在设计上获得了更大的灵活性。

    3 代理模式UML图

    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。 代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后 续处理。

    latex-table

    4 代理模式实例

    以下以《Java与模式》中的示例为例:

    // 抽象角色:
    abstract public class Subject {
        abstract public void  request();
    }
    
    // 真实角色:实现了Subject的request()方法
    public class  RealSubject  extends  Subject  {
      public  RealSubject()  { }
    
      public void  request()  {
         System.out.println( " From real subject. " );
        }
    }
    
    // 代理角色:
    public class  ProxySubject  extends  Subject  {
      // 以真实角色作为代理角色的属性
      private  Subject realSubject;
    
      public  ProxySubject(Subject realSubject)  {this.realSubject = realSubject }
    
      // 该方法封装了真实对象的request方法
      public void  request()  {
         preRequest();
         realSubject.request();  // 此处执行真实对象的request方法
         postRequest();
      }
      ...
    }
    
    // 客户端调用:
    RealSubject real = new RealSubject();
    Subject sub = new  ProxySubject(real);
    Sub.request();
    

    由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用 ProxySubject来代理 RealSubject类,同样达到目的,同时还封装了其他方法 (preRequest(),postRequest()),可以处理一些其他问题。

    另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其 作为代理对象的内部属性。但是实际使用时,如果某一个代理要应用于-批真实角色,毎个 真实对象必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不 知道真实角色,该如何使用编写代理类呢?这个问题可以通过java的动态代理类来解决。

    5 java动态代理

    所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一 组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实 例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工 作。

    5.1 java动态代理UML图

    latex-table

    java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

    1. Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理 类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽 象方法在代理类中动态实现。
    2. Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject。
    3. Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
    4. Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个 代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
    5. Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用 (可使用被代理类的在Subject接口中声明过的方法)。

    在使用动态代理类时,我们必须实现InvocationHandler接口,以第一节中的示例为例:

    // 抽象角色(之前是抽象类,此处应改为接口):
    public  interface Subject {
      abstract  public  void request();
    }
    
    // 具体角色RealSubject:
    public  class RealSubject implements Subject {
      public RealSubject() {}
    
      public  void request() {
        System.out.println( " From real subject. " );
     }
    
    }
    
    // 代理处理器:
    import java.lang.reflect.Method;
    import java.lang.reflect.InvocationHandler;
    
    public  class DynamicSubject implements InvocationHandler {
      private Object sub;
      public DynamicSubject() {}
    
      public DynamicSubject(Object obj) {
        sub = obj;
      }
    
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println( " before calling "  + method);
        method.invoke(sub,args);
    
        System.out.println( " after calling "  + method);
        return  null ;
      }
    }
    

    该代理类的内部属性为Object类,实际使用时通过该类的构造函数DynamicSubject(Object obj)对其赋值;此外,在该类还实现了invoke方法,该方法中的

    method.invoke(sub,args);

    其实就是调用被代理对象的将要被执行的方法,方法参数sub是实际的被代理对象,args为执 行被代理对象相应操作所需的参数。通过动态代理类,我们可以在调用之前或之后执行一些 相关操作。

    // 客户端:
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    public class Client {
    
      static public void main(String[] args) throws Throwable {
       RealSubject rs = new RealSubject(); // 在这里指定被代理类
       InvocationHandler ds = new DynamicSubject(rs);
       Class cls = rs.getClass();
    
       // 以下是一次性生成代理
       Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds );
       subject.request();
      }
    }
    
    // 程序运行结果:
    before calling public abstract void Subject.request()
    From real subject.
    after calling public abstract void Subject.request()
    

    通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口 (Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实 现了非常灵活的动态代理关系。

    6 代理模式与装饰者模式的区别

    代理模式和装饰者模式很像,在典型的例子上,如spring的AOP、远程代理类、JDK的proxy, 都是代理模式。JDK里的输入/输出器是很典型的装饰器模式!但在有些场景上,对设计模式 入门的新手,还是有点难区分,UML类图基本没区别,都是实现同一个接口,一个类包装另一 个类。 两者的定义:

    • 装饰器模式:能动态的新增或组合对象的行为
    • 代理模式:为其他对象提供一种代理以控制对这个对象的访问

    装饰模式是“新增行为”,而代理模式是“控制访问”。关键就是我们如何判断是“新增行 为”还是“控制访问”。你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大 家就知道是在限制。

    6.1 装饰者模式UML图

    latex-table

    其中类的职责如下:

    1. 抽象构件角色(Project):给出一个接口,以规范准备接收附加责任的对象
    2. 具体构件角色(Employe):定义一个将要接收附加责任的类
    3. 装饰角色(Manager):持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口
    4. 具体装饰角色(ManagerA、ManagerB):负责给构件对象“贴上”附加的责任

    6.2 形象说明

    代理模式:在不改变接口的前提下,控制对象的访问

    例子:孙悟空扮演并代替高家三小姐

    孙悟空扮演高家三小姐,所以可以说孙悟空与高家三小姐具有共同的接口。如果猪八戒只想 见见高家三小姐的娇好面容,或者谈谈天说说地,那么高家三小姐的“代理”孙悟空是允许 的,但猪八戒想亲亲嘴,那么是不行的。这是保护代理模式的应用。只有代理对象认为合适 时,才会将客户端的请求传递给真实主题对象。

    装饰模式:在不改变接口的前提下,动态扩展对象的功能

    孙悟空有七十二般变化,在二郎神眼里,他永远是那只猢狲。装饰模式以对客户透明的方式 动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有 什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。他的每 一种变化都给他带来一种附加的本领。他变成鱼儿时,就可以到水里游泳;他变成雀儿时, 就可以在天上飞行。而不管悟空怎么变化,在二郎神眼里,他永远是那只猢狲。装饰模式以 对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在 装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能 加以扩展。

    困难是老天给我们提高的机会,坚定不移勇敢地去攻克,不要退缩,加油!
  • 相关阅读:
    ASE19团队项目 beta阶段 model组 scrum report list
    ASE19团队项目 beta阶段 model组 scrum7 记录
    ASE19团队项目 beta阶段 model组 scrum6 记录
    ASE19团队项目 beta阶段 model组 scrum5 记录
    ASE19团队项目 beta阶段 model组 scrum4 记录
    ASE19团队项目 beta阶段 model组 scrum3 记录
    ASE19团队项目 beta阶段 model组 scrum2 记录
    ASE19团队项目 beta阶段 model组 scrum1 记录
    【ASE模型组】Hint::neural 模型与case study
    【ASE高级软件工程】第二次结对作业
  • 原文地址:https://www.cnblogs.com/merlini/p/3892004.html
Copyright © 2011-2022 走看看