一。首先需要了解下面3个类的API:java系统支持的代理就是这3个类+反射来实现。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
二。下面的这段测试代码,目的就是:将一个ArrayList对象的操作进行代理,每个method前后都输出一行字符串。
核心代码是
Class clz = ArrayList.class;
Object proxyed_Object = Proxy.newProxyInstance(clz.getClassLoader(),
clz.getInterfaces(), new MyInvocationHandle(new ArrayList(10)));
生成一个ArrayList的代理的对象。
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; /** * DEMO:测试java中的代理 * * 代理了一个ArrayList对象,并在调用方法前后各加了一个systemout输出 * @author wei.songw * */ public class MyInvocationHandle implements InvocationHandler { //对代理对象的引用. private List aList; /** * 构造器。 * @param list 代理对象 */ public MyInvocationHandle(Object list) { this.aList = (List) list; } /** * InvocationHandler的方法实现 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法前调用,插入一段消息 System.out.println("before : "+method.getName()); //调用方法. Object object = method.invoke(aList, args); //方法后调用,插入一段消息 System.out.println("after : "+method.getName()); return object; } public static void main(String[] args) { //需要代理一个ArrayList对象,因此按照API构造一个Proxy对象 //,同时也初始化了处理Proxy的MyInvocationHandle对象 Class clz = ArrayList.class; Object proxyed_Object = Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new MyInvocationHandle(new ArrayList(10))); //TEST1:查看一下代理生成类的接口??? // Class[] itfs = proxyed_Object.getClass().getInterfaces(); // for (int i = 0; i < itfs.length; i++) { // System.out.println(itfs[i].getName()); // } //注意!这里操作的是代理类! List list = (List)proxyed_Object; list.add(Integer.valueOf(10)); //TEST2:输出一下list的大小,确认add方法被调用 // System.out.println(list.size()); } }
输出如下:
before : add
after : add
如果将TEST2段代码去掉注释,可以看到如下输出:
before : add
after : add
before : size
after : size
1
证明add方法确实被调用了。
三。代理模式的结构图(UML+时序图)
通过UML图可以看到代理类和被代理实际对象实现同一接口/或者一个抽象类,因此外部调用Subject,是感觉不到代理类的存在。
问题出现了:上面的测试代码中,被代理ArrayList对象,并未与调用者MyInvocationHandle实现同样的接口 。那是怎么实现代理模式的呢?
看Proxy类的文档,写到:
/** * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. This method is equivalent to: *
* Proxy.getProxyClass(loader, interfaces). * getConstructor(new Class[] { InvocationHandler.class }). * newInstance(new Object[] { handler }); ** *
Proxy.newProxyInstance
throws * IllegalArgumentException
for the same reasons that * Proxy.getProxyClass
does. * * @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class * to implement * @param h the invocation handler to dispatch method invocations to * @return a proxy instance with the specified invocation handler of a * proxy class that is defined by the specified class loader * and that implements the specified interfaces * @throws IllegalArgumentException if any of the restrictions on the * parameters that may be passed to getProxyClass
* are violated * @throws NullPointerException if the interfaces
array * argument or any of its elements are null
, or * if the invocation handler, h
, is * null
*/
返回一个实现指定接口的代理类实例,并绑定方法调用到一个指定的invocation handler.
如果将TEST1段注释去掉,可以看到这个代理对象实现了如下接口:
java.util.List java.util.RandomAccess java.lang.Cloneable java.io.Serializable
并且Proxy.newProxyInstance等同于
* Proxy.getProxyClass(loader, interfaces).
* getConstructor(new Class[] { InvocationHandler.class }).
* newInstance(new Object[] { handler });
这里是部分源码:
for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } }
通过时序图可以看到,外部Client和代理类打交道,而代理类在调用实际对象时可以增加一些有益的操作。