qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

Java 代理

代理模式UML类图

 


代理模式

1. 静态代理

 

Java代码 复制代码 收藏代码
  1. /**    
  2.  * 为被代理的类提供一个接口,是为了提高代理的通用性,凡是实现了该接口的类,都可以被代理    
  3.  * 这里其实就是运用了java面向对象的多态性    
  4.  */     
  5. public interface IHello {      
  6.     public void sayHello();      
  7. }      
  8. /**    
  9.  * 被代理的类,最根本的想法就是想用另外一个类来代理这个类,给这个类添加一些额外的东西    
  10.  * 我们只需要创建另外一个类引用这个类就行了    
  11.  */     
  12. public class Hello implements IHello      
  13. {      
  14.     public void sayHello(){      
  15.         System.out.println("被代理的方法");      
  16.     }      
  17. }     
  18. /**    
  19.  * 静态代理类,其实就是(被代理类的)接口的另外一种实现,    
  20.  * 用来代替原来的被代理类    
  21.  * @author qiuxy    
  22.  */     
  23. public class StaticProxy implements IHello {    
  24.     IHello hello;      
  25.     public StaticProxy(IHello hello) {      
  26.         this.hello = hello;      
  27.     }      
  28.     /**    
  29.      * 重新实现了sayHello()方法,这种实现其实就是在被代理类实现该方法中添加一些额外的东西, 以实现代理的作用    
  30. */     
  31.     public void sayHello() {      
  32.         System.out.println("在被代理的对象之前执行");      
  33.         // 被代理的对象执行方法      
  34.         hello.sayHello();      
  35.         System.out.println("在被代理的对象之前执行");      
  36.     }      
  37. }     
  38. /**    
  39.  * 测试类    
  40.  * @author qiuxy    
  41.  */     
  42. public class Test {      
  43.     public static void main(String[] args) {      
  44.         //产生一个被代理对象,只要实现了Ihello接口的对象,都可以成为被代理对象      
  45.         //这里就是利用接口的好处,但这个也有局限性,就是只限于某种接口      
  46.         IHello hello = new Hello();      
  47.         //产生一个代理对象      
  48.         StaticProxy sp = new StaticProxy(hello);      
  49.         //执行代理对象的sayHello()方法,这个方法在被代理的方法前后添加了其他代码      
  50.         sp.sayHello();      
  51.     }      
  52. }    

 

2. 动态代理

 

Java代码 复制代码 收藏代码
  1. import java.lang.reflect.InvocationHandler;      
  2. import java.lang.reflect.Method;      
  3. /**    
  4.  * 代理处理器,这个代理处理器实现了InvocationHandler这个接口,    
  5.  * 这个接口的提供了一个invoke方法,并且传入三个参数(被代理的对象,被代理的方法,方法的参数数组)    
  6.  * 这个invoke方法会在被代理的方法之前被调用    
  7.  */     
  8. public class MyProxyHandler implements InvocationHandler {      
  9.     //被代理的对象      
  10.     Object delegate;       
  11.     //构造函数,在构在这个代理处理器时,传入要被代理的对象      
  12.     public MyProxyHandler(Object delegate) {           
  13.     this.delegate = delegate;      
  14.     }      
  15.     //被代理的方法之前会调用的方法      
  16.     public Object invoke(Object proxy, Method method, Object[] args)   throws Throwable {    
  17.         System.out.println("我在被代理的方法之前执行");      
  18.         //被代理的方法开始执行      
  19.         method.invoke(delegate, args);      
  20.         System.out.println("我在被代理的方法之后执行");      
  21.         return null;      
  22.     }      
  23. }     
  24. import java.lang.reflect.Proxy;      
  25. /**    
  26.  * 测试类    
  27.  */     
  28. public class Test      
  29. {      
  30.     public static void main(String[] args)      
  31.     {      
  32.         Hello hello = new Hello();      
  33.         //产生一个代理处理器对象      
  34.         MyProxyHandler mph = new MyProxyHandler(hello);      
  35.         //通过反射获得代理类,这里必须转换成代理类的接口类型      
  36.         //这个代理对象其实是java动态生成一个被代理类的接口的另外一个实现类      
  37.         IHello myProxy = (IHello)Proxy.newProxyInstance(hello.getClass().getClassLoader()  , hello.getClass().getInterfaces(), mph);      
  38.         //代理类执行方法      
  39.         myProxy.sayHello();      
  40.     }      
  41.      
  42. }  

    代理模式

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

    代理模式一般涉及到的角色有:

    抽象角色:声明真实对象和代理对象的共同接口;

    代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

    真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。(参见文献1)

    以下以《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 RealSubject realSubject;  //以真实角色作为代理角色的属性

          

           public ProxySubject()

           {

           }

     

           public void request()  //该方法封装了真实对象的request方法

           {

            preRequest(); 

                  if( realSubject == null )

            {

                         realSubject = new RealSubject();

                  }

            realSubject.request();  //此处执行真实对象的request方法

            postRequest();

           }

     

        private void preRequest()

        {

            //something you want to do before requesting

        }

     

        private void postRequest()

        {

            //something you want to do after requesting

        }

    }

    客户端调用:

    Subject sub=new ProxySubject();

    Sub.request();

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

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

     

    2.动态代理类

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

    (1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, J2EEjava语言JDK1.4APIjavalangObject.html">Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。


    (2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:

    Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。

    Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

    Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

     

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

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

    抽象角色(之前是抽象类,此处应改为接口):

    public interface Subject

    {

        abstract public void request();

    }

    具体角色RealSubject:同上;

     

    代理角色:

    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();

          //以下是分解步骤

          /*

          Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces()) ;

          Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class});

          Subject subject =(Subject) ct.newInstance(new Object[]{ds});

         */

         //以下是一次性生成

          Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),

                                     cls.getInterfaces(),ds );

     

          subject.request();

    }

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

 


posted on 2011-09-22 17:06 顺其自然EVO 阅读(258) 评论(0)  编辑  收藏

<2011年9月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜