一。首先需要了解下面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和代理类打交道,而代理类在调用实际对象时可以增加一些有益的操作。