→阿童沐

財富==支撐一個人生存多長時間的能力!

导航

<Java设计模式> 代理模式-

代理模式的作用:

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

  在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

  中介不仅可以帮助客户端完成客户想要完成的事情,还可以完成中介自己想要做的事情。


代理模式中涉及到的角色:     六十五-18:00

  1、抽象角色:声明真实对象和代理对象的共同接口;(例如:中介和房主两者均可以完成租房子这件事情,因此两者含有共同的接口,接口中含有租房子这个方法)

  2、代理角色代理角色对象内部含有对真是对象的引用,从而可以操作真实对象(聚合包容关系),同时代理对象提供与真是对象相同的接口以便在任何时候都能代替真实的对象。

          同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真是对象进行封装。(例如:中介)

  3、真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。(例如:房主)

实例讲解:  六十五-24:00


代理模式举例:

  1、抽象角色(可用抽象类或者接口来实现):

package cn.edu.bupt.proxy;

public interface Subject
{
    void request();
}

 

  2、真实角色(需要实现抽象角色):

package cn.edu.bupt.proxy;

public class RealSubject implements Subject
{
    @Override
    public void request()
    {
        System.out.println("From real subject");
    }
}

 

  3、代理角色(仍然需要实现抽象角色):

package cn.edu.bupt.proxy;

public class ProxySubject implements Subject
{
    private RealSubject realSubject = null;    //代理角色内部引用了真实角色
    
    private void preRequest()
    {
        System.out.println("pre request");
    }
    
    private void postRequest()
    {
        System.out.println("post request");
    }
    
    
    @Override
    public void request()
    {
        //代理在真实角色操作之前所附加的操作
        this.preRequest();
        
        //代理所完成的主要任务代码写于此
        if (null == this.realSubject)
        {
            this.realSubject = new RealSubject();
        }
        this.realSubject.request();        //代理通过真实角色完成相同的事情
        
        //在真是角色操作之后所附加的操作
        this.postRequest();
    }

}

 

 

  4、客户端对代理角色的直接调用,同时对真实对象的间接调用:

package cn.edu.bupt.proxy;

public class Client
{
    public static void main(String[] args)
    {
        Subject sub = new ProxySubject();
        sub.request();
    }
} 

打印结果:

 

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

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


 

Java动态代理类:    六十五-30:00

一般主要涉及一下两个类,他们均位于java.lang.reflect包下:

  1、Interface InvocationHandler:该接口中仅仅定义了一个方法:

    public Object invoke(Object obj, Method method, Object[] args);

  在实际使用时,第一个参数obj一般指代理类,method是指被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

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

    1> protected Proxy(InvocationHandler h)

      构造函数用于给内部h赋值。

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

      获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

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

      返回一个代理类的实例,返回后的代理类可以被当做代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

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

也就是说,在使用动态代理类的时候,我们必须实现InvocationHandler接口,这里面提供我们实质性的代码。


 利用动态代理改写上例:

 抽象角色(此时一定要使用接口实现抽象角色)

package cn.edu.bupt.dynamicproxy;

public interface SubjectInterface
{
    void request(String word);
    void request2(String word);
}

 

 

 真实角色, 房主(一定要实现抽象角色所对应的接口)

package cn.edu.bupt.dynamicproxy;

public class RealSubject implements SubjectInterface
{
    @Override
    public void request(String message)
    {
        System.out.println("From real subject. Message: " + message);
    }
    @Override
    public void request2(String message)
    {
        System.out.println("From real subject2. Message: " + message);
    }
}

 

 动态处理器,用于自动生成代理类(要实现java.lang.reflect.InvocationHandler接口,这里放置的是代理中业务逻辑的主要代码)

package cn.edu.bupt.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个代表真实角色的对象
 * 此外,该类还是先了InvocationHandler中的invoke方法,该方法中的method.invoke其实就是调用被代
 * 理对象的将要执行的方法,方法参数是sub,通过动态代理类我们可以在执行真实对象的方法前后加入自己的
 * 一些额外的业务逻辑代码
 * @author Medow
 *
 */

public class DynamicHandler implements InvocationHandler
{
    private Object sub;
    
    public DynamicHandler(Object sub)
    {
        this.sub = sub;
    }
    
    // proxy参数一般情况下是用不上的
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        this.beforeInvoke();
        
        // args是在通过Proxy动态生成的代理类对象传递进来的,见Client类的19,20行
        Object obj = method.invoke(this.sub, args);
        
        this.afterInvoke();
        
        return obj;
    }
    
    private void beforeInvoke()
    {
        System.out.println("before calling.");
    }
    
    private void afterInvoke()
    {
        System.out.println("after calling.");
    }
}

 

 客户端(这里面创建真实角色的对象,通过Proxy动态创建代理,然后通过动态代理间接的调用真实角色中的方法)

 1 package cn.edu.bupt.dynamicproxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Proxy;
 5 
 6 public class Client
 7 {
 8     public static void main(String[] args)
 9     {
10         RealSubject realSubject = new RealSubject();
11         InvocationHandler handler = new DynamicHandler(realSubject);
12 
13         // 下面的代码在运行时根据需要生成代理,注意需要传入真实角色对象的类加载器
14         Class<?> classType = handler.getClass();
15         SubjectInterface subjectInterface = (SubjectInterface) Proxy.newProxyInstance(classType
16                 .getClassLoader(), realSubject.getClass().getInterfaces(), handler);
17         
18         // 生成的代理类的对象是通过之前声明的SubjectInterface接口进行调用的
19         subjectInterface.request("request1");
20         subjectInterface.request2("request2");
21         
22         System.out.println("subject: " + subjectInterface.getClass().getInterfaces()[0].toString());
23     }
24 }

 

 

注意理解:见  六十六-38:00

通过subjectInterface接口调用request方法时,request方法会通过反射机制传递至InvocationHandler实现类中的invoke方法的method参数中,而request方法的实际参数"request"则是传递至invoke方法中的args形式参数中,而实际调用的对象则是构造handler时传入的真实角色对象RealSubject类的实例对象this.sub,这样,通过method.invoke(this.sub, args),就完成了对目标方法的调用。

同理,subjectInterface.request2("request2")方法调用也是这样的实现机制,另外,生成的动态代理对象将会实现参数realSubject.getClass().getInterfaces()所确定的interface的Class对象的数组,则样生成的动态代理类可以转换成为目标接口,例如实现了A和B接口,则生成的动态代理类可以强制转换成为A或B类型。

需要说明的是,代理类对象是通过Proxy类在程序运行时动态生成的,而不是通过代码敲入实现的,一定注意。

 当动态生成一个代理类的对象的时候,通过接口(强制转换所得)执行目标方法的时候(例如:上例中的subjectInterface.request("request1") 和 subjectInterface.request("request2")),均会转而执行所实现的InvocationHandler类中的invoke方法,而对于invoke方法的组织,则可以自由执行,哪怕没有RealSubject中的方法调用也可以,这时候将不会通过代理执行真实对象的目标方法(具体怎样执行流程是JDK内部实现的,在这里不用去管具体细节)。如下:

 1 package cn.edu.bupt.dynamicproxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 /**
 7  * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个代表真实角色的对象
 8  * 此外,该类还是先了InvocationHandler中的invoke方法,该方法中的method.invoke其实就是调用被代
 9  * 理对象的将要执行的方法,方法参数是sub,通过动态代理类我们可以在执行真实对象的方法前后加入自己的
10  * 一些额外的业务逻辑代码
11  * @author Medow
12  *
13  */
14 
15 public class DynamicHandler implements InvocationHandler
16 {
17     private Object sub;
18     
19     public DynamicHandler(Object sub)
20     {
21         this.sub = sub;
22     }
23     
24     // proxy参数一般情况下是用不上的
25     @Override
26     public Object invoke(Object proxy, Method method, Object[] args)
27             throws Throwable
28     {
29         this.beforeInvoke();
30         
31         // args是在通过Proxy动态生成的代理类对象传递进来的,见Client类的19,20行
32         //Object obj = method.invoke(this.sub, args);
33         
34         this.afterInvoke();
35         
36         return null;
37     }
38     
39     private void beforeInvoke()
40     {
41         System.out.println("before calling.");
42     }
43     
44     private void afterInvoke()
45     {
46         System.out.println("after calling.");
47     }
48 }

 

 如果将Line32 去掉,则也可以 正常运行,只不过不会调用realSubject对象中的request和request2方法,下图为执行结果:

 

 


 

动态代理创建步骤: 

 1、创建一个实现接口InvocationHandler的类,它必须实现invoke方法,同时可传入真实角色的一个对象;

 2、创建真实角色(被代理的类)的类以及相应的接口;

 3、通过Proxy的静态方法

  newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

   在运行时动态的创建一个代理

 4、将动态创建的代理强制转换成为相应的接口,并通过接口调用真实类中的方法。


 

 例:VetorProxy

 1 package cn.edu.bupt.dynamicproxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 import java.util.Iterator;
 7 import java.util.List;
 8 import java.util.Vector;
 9 
10 public class VectorProxy implements InvocationHandler
11 {
12     private Object realSubject;
13 
14     public static Object factory(Object realSubject)
15     {
16         Class<?> realClassType = realSubject.getClass();
17         return Proxy.newProxyInstance(realClassType.getClassLoader(),
18                 realClassType.getInterfaces(), new VectorProxy(
19                         realSubject));
20     }
21 
22     public VectorProxy(Object realSubject)
23     {
24         this.realSubject = realSubject;
25     }
26 
27     @Override
28     public Object invoke(Object proxy, Method method, Object[] args)
29             throws Throwable
30     {
31         // 调用真实对象方法之前插入的业务逻辑代码
32         this.beforeInvoke(method);
33         
34         if (null != args)
35         {
36             for (Object arg : args)
37             {
38                 System.out.println("argument passed : " + arg);
39             }
40         }
41         else
42         {
43             System.out.println("no argument passed!");
44         }
45         
46         Object returnObject = method.invoke(this.realSubject, args);
47         
48         // 调用真实对象方法之后所插入的业务逻辑代码
49         this.afterInvoke(method);
50         
51         return returnObject;
52     }
53     
54     private void beforeInvoke(Method method)
55     {
56         System.out.println("before calling: " + method);
57     }
58     
59     private void afterInvoke(Method method)
60     {
61         System.out.println("after calling: " + method);
62     }
63 
64     
65     //--------for test-----------
66     public static void main(String[] args)
67     {
68         List<String> list = (List<String>)factory(new Vector<String>());
69         
70         
71         list.add("hello");        //将会调用invoke()方法,传入一个参数 String:"hello"
72         list.add("world");        //将会调用invoke()方法,传入一个参数 String:"world"
73         
74         // 将会调用一次invoke()方法,而且没有传入任何参数
75         for (Iterator<String> itr = list.iterator(); itr.hasNext();)
76         {
77             itr.next();
78         }
79     }
80 }

 

 执行结果如下:

 分析:见六十七-16:00


 

静态代理与动态代理的区别与联系:  六十八-10:00

 静态代理:

其中Subject是共同的接口,Real实例和Proxy实例均实现了该接口,但是Proxy实例内部持有Real实例的一个引用,也就是说他们俩是聚合包容的关系,因此,每存在一个Real实例,都要编写相应的Proxy实例来实现对真实角色的代理功能,这样会出现很多的代理角色类.

动态代理:

 角色均是相同的.

 真实角色和抽象角色两者形式和意义与静态代理类均相同.

 代理角色意义同静态代理,但是实现方式不相同.

 代理类需要实现共同的接口,在动态代理中是通过

 static Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

 中的第二个参数指定的,这样在运行时生成的动态代理类就实现了第二个参数所指定的接口列表中的每一个接口.

 而通过接口调用真实角色中的方法的时候,是通过代理进行调用的,但是代理内部的主要业务逻辑代码是通过第三个参数InvocationHandler指定的,因此我们需要人为的实现InvocationHandler接口,并传入到该方法中,InvocationHandler中的invoke方法是编写业务逻辑代码的主要部位.

 

动态代理的好处:

如果采用静态代理比较麻烦的是,对于接口中的每一个方法,在代理类中均要有相应的方法实现,如果接口中的方法成百上千的出现的话,对于代理类中,需要实现对应的成百上千个方法的实现,这样对于我肯定是不想看见的,但是如果采用动态代理的话,我仅仅需要实现一个InvocationHandler中的invoke方法,然后通过抽象角色的接口进行访问就能实现对真实角色类中的大量方法的访问,这是非常方便的。

但是,代理本身并不进行实际的业务逻辑操作,对代理的调用,均会转移至InvocationHandler中唯一的invoke()方法进行调用,因此业务逻辑代码需要在invoke()方法中编写.

当通过接口引用调用动态生成的代理对象的时候,如下图:

这里的subject就是接口引用,他指向了程序运行时动态生成的代理对象:    六十八-24:00

当执行subject.request()时, request()方法就传入了invoke()方法中的Method类型的参数中; request()中的实际参数(这里的实际参数为null) 就传入了invoke()方法中的Object[]类型的. 这样在invoke()方法中就可以间接完成了对request()方法的调用,但是是否调用完全取决于你的业务逻辑代码。


 

最后一例:

抽象角色(接口):

package cn.edu.bupt.dynamicproxy;

public interface Foo
{
    void doAction();
}

 

两个真实角色(接口实现类):

package cn.edu.bupt.dynamicproxy;

public class FooImpl1 implements Foo
{
    @Override
    public void doAction()
    {
        System.out.println("doAction in : " + this.getClass().getName());
    }
}
package cn.edu.bupt.dynamicproxy;

public class FooImpl2 implements Foo
{

    @Override
    public void doAction()
    {
        System.out.println("doAction in : " + this.getClass().getName());
    }

}

 

InvocationHandler(实现java.lang.reflect.InvocationHandler接口):

package cn.edu.bupt.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class CommonInvocationHandler implements InvocationHandler
{
    private Object target = null;
    
    public CommonInvocationHandler(){}
    
    public CommonInvocationHandler(Object target)
    {
        super();
        this.target = target;
    }

    public void setTarget(Object target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        if (null != this.target)
        {
            return method.invoke(this.target, args);
        }
        return null;
    }

}

 

客户端(动态生成代理类,并传入InvocationHandler实现类):

package cn.edu.bupt.dynamicproxy;

import java.lang.reflect.Proxy;

public class Client2
{
    public static void main(String[] args)
    {
        Foo foo = null;        //抽象角色
        
        CommonInvocationHandler handler = new CommonInvocationHandler();
        
        // 传入FooImpl1()
        foo = new FooImpl1();
        handler.setTarget(foo);
        foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, handler);
        foo.doAction();
        
        // 传入FooImpl2()
        foo = new FooImpl2();
        handler.setTarget(foo);
        foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, handler);
        foo.doAction();
    }
}
    

 

执行结果如下:


 

总结:

对于动态代理类的理解有助于框架的学习,Spring框架中大量使用了动态代理类,貌似是AOP吧。

 

 

posted on 2012-05-03 19:42  阿童沐  阅读(161)  评论(0)    收藏  举报