[size=large][color=indigo][b]代理模式 [/b][/color][/size]

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

代理模式一般涉及到的角色有:
[list]
[*]抽象角色:声明真实对象和代理对象的共同接口;
[*]代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
[*]真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
[/list]
以下举个简单的例子:
抽象角色:

abstract class Subject{ 
       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() { }

  //该方法封装了真实对象的request方法
  public void 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()),可以处理一些其他问题。



代理模式可以用下面这个UML图表示:


[img]http://dl.iteye.com/upload/attachment/531731/96488bc0-478c-37ac-9a7a-52314ce5fac8.png[/img]


为了保持行为的一致性,代理类(ProxySubject)和委托类(RealSubject)通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。



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



[color=indigo][size=large][b]JDK动态代理[/b][/size][/color]


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


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


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



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


[list]


[*]Protected Proxy(InvocationHandler h):构造函数,给内部的h赋值。


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


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


[/list]


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


在使用动态代理类时,我们必须实现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.");
  }
}



代理处理器:


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);//其实就是调用被代理对象的将要被执行的方法,方法参数sub是实际的被代理对象,args为执行被代理对象相应操作所需的参数。

   System.out.println("after calling " + method);
   return null;
 }
}



客户端:


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类)也可以动态改变,从而实现了非常灵活的动态代理关系。



下面我们来看看在 Proxy类的newProxyInstance方法中发生了什么,来看源码:


public static Object newProxyInstance(ClassLoader loader,
					  Class<?>[] interfaces,
					  InvocationHandler h)
	throws IllegalArgumentException
 {
	if (h == null) {
	    throw new NullPointerException();
	}
        //【1】创建代理类
	Class cl = getProxyClass(loader, interfaces);

	try {
            //【2】创建代理类对象
	    Constructor cons = cl.getConstructor(constructorParams);
	    return (Object) cons.newInstance(new Object[] { h });
	} catch (NoSuchMethodException e) {
	    throw new InternalError(e.toString());
	} catch (IllegalAccessException e) {
	    throw new InternalError(e.toString());
	} catch (InstantiationException e) {
	    throw new InternalError(e.toString());
	} catch (InvocationTargetException e) {
	    throw new InternalError(e.toString());
	}
    }



先来看看【1】代码处,调用了getProxyClass方法创建了代理类,Proxy的public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)方法比较长(我数了一下算上注释有200+行),这个方法的具体分析可以参考[url]http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/[/url]


其实这个方法做的事情就是创建代理类,这个代理类实现了interfaces接口,使用的是loader类装载器。


在这个例子中,创建的代理类实现了Subject接口。



再来看看【2】代码处,创建了一个代理类的对象,并将一个InvocationHandler的对象传入。这实际调用的是Proxy的构造方法:


protected Proxy(InvocationHandler h) {
	this.h = h;
 }



这也说明了另一个问题,这个创建的代理类是Proxy的子类。


代理类的继承结构如下:


[img]http://dl.iteye.com/upload/attachment/531736/9e384e3f-241a-3f96-89d7-fe58c2933411.png[/img]



说到这插播一段java.lang.ClassLoader的介绍:它是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。


每次生成动态代理类对象时都需要指定一个类装载器对象。



下面再来看看InvocationHandler接口的源码:


public interface InvocationHandler {
  public Object invoke(Object proxy, Method method, Object[] args)
	throws Throwable;
}



invoke方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行



那么,Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds )所创建的这个代理类到底是什么样子的呢?下面就来揭开它神秘的面纱(此处参考了[url]http://hi.baidu.com/malecu/blog/item/45d4952b31bc0e27d52af17a.html[/url]):


//实现了Subject接口,并继承了Proxy 
public final class $Proxy0 extends Proxy implements Subject{

private static Method m0;
private static Method m1;
private static Method m2;
private static Method m3;

static {
   try {
    m1 = Class.forName("java.lang.Object").getMethod("equals",
      new Class[] { Class.forName("java.lang.Object") });
    m0 = Class.forName("java.lang.Object").getMethod("hashCode",
      new Class[0]);
    m3 = Class.forName("com.itec.test.Subject").getMethod("request",
      new Class[0]);
    m2 = Class.forName("java.lang.Object").getMethod("toString",
      new Class[0]);
   } catch (NoSuchMethodException nosuchmethodexception) {
    throw new NoSuchMethodError(nosuchmethodexception.getMessage());
   } catch (ClassNotFoundException classnotfoundexception) {
    throw new NoClassDefFoundError(classnotfoundexception.getMessage());
   }
}

//和Proxy的构造方法相对应的
public $Proxy0(InvocationHandler invocationhandler) {
   super(invocationhandler);
}

@Override
public final boolean equals(Object obj) {
   try {
    return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
      .booleanValue();
   } catch (Throwable throwable) {
    throw new UndeclaredThrowableException(throwable);
   }
}

@Override
public final int hashCode() {
   try {
    return ((Integer) super.h.invoke(this, m0, null)).intValue();
   } catch (Throwable throwable) {
    throw new UndeclaredThrowableException(throwable);
   }
}

public final void request() {
   try {
    super.h.invoke(this, m3, null);//调用的是InvocationHandler对象的invoke方法
    return;
   } catch (Error e) {
   } catch (Throwable throwable) {
    throw new UndeclaredThrowableException(throwable);
   }
}

@Override
public final String toString() {
   try {
    return (String) super.h.invoke(this, m2, null);
   } catch (Throwable throwable) {
    throw new UndeclaredThrowableException(throwable);
   }
}
}



在得到这个代理类之后,接着把得到的$Proxy0实例强制转换成Subject。


当执行subject.request();方法时,就调用了$Proxy0类中的request()方法。


在request方法中,调用父类Proxy中的h的invoke()方法,即InvocationHandler.invoke();


可以明显看出,这个InvocationHandler.invoke()方法使用的就是回调的机制。(如果对回调的内容感兴趣,请参考这篇帖子[url]http://hellosure.iteye.com/blog/1130176[/url])



好了,现在来对比一下前面提到的这两个例子,对于后一个例子来说:代理类$Proxy0就相当于是前一个例子中的ProxySubject类,只不过这个代理类的创建需要用到Java.lang.reflect.Proxy,并且实际的代理方法需要借助到InvocationHandler来完成回调。


那么,[b]使用动态代理的好处[/b]显而易见:


Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。



[color=indigo][size=large][b]CJLIB动态代理[/b][/size][/color]


JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。


抽象角色:


public interface Subject {
   abstract public void request();
}



具体角色RealSubject:


public class RealSubject {//注意没有实现Subject 接口
  public RealSubject(){}

  public void request(){
    System.out.println("From real subject.");
  }
}



代理处理器:


public class SubjectCglib implements MethodInterceptor {  
    private Object target;  

    public Object getInstance(Object target) {  
        this.target = target;  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(this.target.getClass());  
        // 回调方法  
        enhancer.setCallback(this);  
        // 创建代理对象  
        return enhancer.create();  
    }  

    @Override  
    // 回调方法  
    public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {  
        System.out.println("before calling " + method);  
        proxy.invokeSuper(obj, args);  
        System.out.println("after calling " + method);  
        return null; 
    }  
}



客户端:


public class TestCglib {  
    public static void main(String[] args) {  
        SubjectCglib cglib=new SubjectCglib ();  
        RealSubject subjectCglib=(RealSubject)cglib.getInstance(new RealSubject());  
        subjectCglib.request();  
    }  
}




[color=indigo][size=large][b]反射机制简介[/b][/size][/color]


动态代理的实现大量使用了Java反射机制,这个部分和本文主题关系不大,算是一个小小的补充,老鸟请略过,菜鸟瞄一下就行了,可能会帮助你理解。


JAVA语言中的反射机制:


在Java 运行时 环境中,对于任意一个类,能否知道这个类有哪些属性和方法?


对于任意一个对象,能否调用他的方法?这些答案是肯定的,这种动态获取类的信息,以及动态调用类的方法的功能来源于JAVA的反射。从而使java具有动态语言的特性。



JAVA反射机制主要提供了以下功能:


[list]


[*]在运行时判断任意一个对象所属的类


[*]在运行时构造任意一个类的对象


[*]在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)


[*]在运行时调用任意一个对象的方法(前提都是在运行时,而不是在编译时)


[/list]


Java 反射相关的API简介:位于java。lang。reflect包中


[list]


[*]Class类:代表一个类


[*]Filed类:代表类的成员变量


[*]Method类:代表类的方法


[*]Constructor类:代表类的构造方法


[*]Array类:提供了动态创建数组,以及访问数组的元素的静态方法。该类中的所有方法都是静态方法


[/list]


[b]Class类[/b]


在 java 的Object类中的申明了数个应该在所有的java类中被改写的methods:


hashCode(), equals(),clone(),toString(),getClass()等,其中的getClass()返回一个Class 类型的对象。


Class类十分的特殊,它和一般的类一样继承自Object,其实体用以表达java程序运行


时的 class和 interface,也用来表达 enum,array,primitive,Java Types 以及关键字void,当加载一个类,或者当加载器(class loader)的defineClass()被JVM调用,便产生一个Class对象,


Class是Reflection起源,针对任何你想探勘的class(类),唯有现为他产生一个Class


的对象,接下来才能经由后者唤起为数十多个的反射API。



Java允许我们从多种途径为一个类class生成对应的Class对象。


[list]


[*]运用 getClass():Object类中的方法,每个类都拥有此方法


[*]运用 Class.getSuperclass():Class类中的方法,返回该Class的父类的Class


[*]运用 Class.forName()静态方法:


[*]运用 Class:类名.class


[*]运用primitive wrapper classes的TYPE语法: 基本类型包装类的TYPE,如:Integer.TYPE注意:TYPE的使用,只适合原生(基本)数据类型


[/list]


运行时生成[b]instance[/b]


想生成对象的实体,在反射动态机制中有两种方法,一个针对无变量的构造方法,一个针对带参数的构造方法,如果想调用带参数的构造方法,就比较的麻烦,不能直接调用Class类中的newInstance(),而是调用Constructor类中newInstance()方法,首先准备一个Class[]作为Constructor的参数类型。


然后调用该Class对象的getConstructor()方法获得一个专属的Constructor的对象,最后再准备一个Object[]作为Constructor对象昂的newInstance()方法的实参。在这里需要说明的是 只有两个类拥有newInstance()方法,分别是Class类和Constructor类.


Class类中的newInstance()方法是不带参数的,而Constructro类中的newInstance()方法是带参数的


需要提供必要的参数。


例:


Class c=Class.forName("DynTest");
Class[] ptype=new Class[]{double.class,int.class};
Constructor ctor=c.getConstructor(ptypr);
Object[] obj=new Object[]{new Double(3.1415),new Integer(123)};
Object object=ctor.newInstance(obj);
System.out.println(object);



运行时调用[b]Method[/b]


这个动作首先准备一个Class[]{}作为getMethod(String name,Class[])方法的参数类型,接下来准备一个Obeject[]放置自变量,然后调用Method对象的invoke(Object obj,Object[])方法。



运行时调用[b]Field[/b]内容


变更Field不需要参数和自变量,首先调用Class的getField()并指定field名称,获得特定的Field对象后便可以直接调用Field的 get(Object obj)和set(Object obj,Object value)方法