江苏 无锡 缪小东
写到代理模式这章,不得不提到JDK中的动态代理,它是java语言自身对动态代理的支持,类似于JDK中在java.util包中提供Observable类和Observer接口提供对观察者模式的语言级支持。关于动态代理的好处可以从网络上流行的<<JAVA中用动态代理类实现记忆功能>>、<<使用JAVA中的动态代理实现数据库连接池>>、<<通过JAVA的动态代理机制控制事务>> 、<<用Java动态代理实现AOP>>……看出动态代理的优点――动态地为软件增加功能。应用的文章很多,出于自身的好奇我们来研究JDK中动态代理的实现吧!这就是本博客所谓的“任何东西只有精通了,才能更好地使用”,有别于所谓的“很好地使用它,然后再去精通它”。下面我们开始对java语言中动态代理的源代码的研究吧!
一、动态代理的相关类
JDK中和动态代理直接相关的主要有InvocationHandler接口和Proxy类。InvocationHandler接口相当于Proxy类的CallBack Interface。
下图为InvocationHandler接口的类图,该接口中仅定义了Object:invoke(Object obj,Method method, Object[] args)一个方法。第一个参数proxy一般是指具体的被代理类,即代理模式中的目标对象;method是被代理的方法,args为该方法的参数数组。该接口在动态代理中由客户实现。
三、Proxy类
Proxy为动态代理类,是动态代理的核心类,其作用类似于代理模式中的代理。从上面的类图可以看出Proxy类中包含的全是静态的方法和成员变量,这是一个纯粹的工具类,因此其源代码中含有一个私有的构造器,在某种意义上可以看作为一个单例模式的特殊情形。(原因为:1.不能实例化;2.提供统一的入口)。这个类中包含很多很有意思的实现,下面我们还是看看其源代码吧!
package java.lang.reflect;
import java.lang.ref.*;
import java.util.*;
import sun.misc.ProxyGenerator; //sun将产生代理对象的类,放在sun.misc包中
public class Proxy implements java.io.Serializable { //实现了Serializable接口,因此可以保存到流中
private static final long serialVersionUID = -2222568056686623797L; //序列化时使用的版本号
//以下的变量用于代理对象创建时,名称各部分的组合
//以下几个成员变量组合在一起,形成被创建的代理对象的名称
//这些被ProxyGenerator创建的代理对象,以此名字,存入当前Proxy对象内部的cache中
//被创建代理对象的名称包含3部分:
// 1.包名proxyPkg ;2.代理对象的名称前缀;3.代理被创建的数目num;
// 该数目表示当前被创建的代理对象是使用ProxyGenerator创建的第n个代理对象
//下面是代理对象的名称前缀
private final static String proxyClassNamePrefix = "$Proxy";
//初始化时,代理被创建的个数为0
private static long nextUniqueNumber = 0;
//由于可能有多个客户同时使用ProxyGenerator创建代理对象,因此必须进行同步
// 此同步过程使代理被创建的数目唯一
private static Object nextUniqueNumberLock = new Object();
//表示某个代理对象正在被创建
private static Object pendingGenerationMarker = new Object();
//代理对象实例化时需要的的构造参数
private final static Class[] constructorParams ={ InvocationHandler.class };
//类转载器的cache
private static Map loaderToCache = new WeakHashMap();
//所有已经被创建的代理对象的集合,每次要创建新的代理对象,都会先到该集合查找是否存在
// 这其实是一个代理对象的缓存
private static Map proxyClasses =Collections.synchronizedMap(new WeakHashMap());
protected InvocationHandler h; //唯一的非静态的成员变量,主要用于protected构造器
protected Proxy(InvocationHandler h) { //protected构造器,给继承提供了可能
this.h = h;
}
private Proxy() { } //工具类,不能实例化,因此为私有构造器
//使用特定的类加载器,加载某个类,从而得到此代理对象的Class
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException{
if (interfaces.length > 65535) { //构造器太多,抛出异常
throw new IllegalArgumentException("interface limit exceeded");
}
Class proxyClass = null; //初始化一个Class
//将interface的名字 collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
Set interfaceSet = new HashSet(); //避免重复
for (int i = 0; i < interfaces.length; i++) {
String interfaceName = interfaces[i].getName(); //得到名称
Class interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader); //使用类装载器转载类
} catch (ClassNotFoundException e) { }
if (interfaceClass != interfaces[i]) { //两者不等抛出异常
throw new IllegalArgumentException(interfaces[i] + " is not visible from class loader");
}
if (!interfaceClass.isInterface()) { //不是接口抛出异常
throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface");
}
if (interfaceSet.contains(interfaceClass)) { //已经存在抛出异常
throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass); //不存在则放入hashset中
interfaceNames[i] = interfaceName; //置换输入中的构造器
}
Object key = Arrays.asList(interfaceNames); //将上面的数组转化为List对象
//以上完成对构造器的处理
Map cache;
synchronized (loaderToCache) { //同步化类装载器的Cache
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap(); //该Cache以类装载器为key,value也为一个Cache
loaderToCache.put(loader, cache);
}
}
synchronized (cache) {
do {
Object value = cache.get(key); //该Cache以Class数组为key以proxyClass为value
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get(); //保存在Cache中的value为Reference
}
if (proxyClass != null) { //代理对象存在
return proxyClass; //返回此proxyClass
} else if (value == pendingGenerationMarker) {
try {
cache.wait(); //其它线程正在创建代理对象,则本线程等待
} catch (InterruptedException e) { }
continue;
} else {
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
try {
String proxyPkg = null; // 代理对象所在的包
for (int i = 0; i < interfaces.length; i++) { //遍历Class数组
int flags = interfaces[i].getModifiers(); //得到Class的修饰符
if (!Modifier.isPublic(flags)) { //public的修饰符
String >
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); //package名称
if (proxyPkg == null) {
proxyPkg = pkg; //
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException( "non-public interfaces from different packages");
}
}
}//for
if (proxyPkg == null) {
proxyPkg = ""; // 默认包名
}
{
long num;
synchronized (nextUniqueNumberLock) { //同步块
num = nextUniqueNumber++; //每次递增1
}
String proxyName = proxyPkg + proxyClassNamePrefix + num; //名称为三者的组合
//以下是创建ProxyClass的过程
byte[] proxyClassFile =ProxyGenerator.generateProxyClass(proxyName, interfaces);
//上面是使用ProxyGenerator将指定名称代理类的.class文件转化为byte数组
//在类加载器中进一步执行此.class文件,在java语言中经常称java为解释型语言
//字节码就是被解释的中间代码,它是独立于平台的,其执行是在虚拟机上
// 有兴趣的朋友多研究研究编译原理、以及虚拟机的实现
// sun已经将其Compiler和VM的源代码开源了,有能力可以研究研究
/ /利用刚才产生的byte数组,使用类加载器产生此ProxyClass
try {
使用类装载器,装载指定名称的代理类
proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
// 将新创建的ProxyClass放入Chche中
proxyClasses.put(proxyClass, null);
} finally {
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference(proxyClass)); //将此Renference放入cache中
} else {
cache.remove(key);
}
cache.notifyAll(); //多线程的wait/notify机制
}
}
return proxyClass;
}
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{
if (h == null) { throw new NullPointerException(); } //InvocationHandler为null抛出异常
Class cl = getProxyClass(loader, interfaces); //使用上面的方法得到一个ProxyClass
try {
Constructor cons = cl.getConstructor(constructorParams); //得到此类的构造器
return (Object) cons.newInstance(new Object[] { h }); //使用Handler构造对象
} 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());
}
}
public static boolean isProxyClass(Class<?> cl) { //判断某个Class是否为ProxyClass
if (cl == null) { throw new NullPointerException(); } //输入为null抛出异常
return proxyClasses.containsKey(cl); //返回该Proxy的Chche中是否存在该Class
}
//得到某个ProxyClass对象对应的InvocationHandler
public static InvocationHandler getInvocationHandler(Object proxy)throws IllegalArgumentException{
if (!isProxyClass(proxy.getClass())) { //该对象不是ProxyClass则抛出异常
throw new IllegalArgumentException("not a proxy instance");
}
Proxy p = (Proxy) proxy; //是ProxyClass则downcast
return p.h; //返回此ProxyClass的成员变量
}
//到目前为止,我们可以猜测在创建ProxyClass时,
//肯定使用该Proxy类的子类或者其protected的构造器中的一种
private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
//使用JNI创建一个Class
}
上面使用多个Cache,以下是这些Cache的层次图:
四、使用动态代理的例子
下面给出一个具体的例子:
//ServiceIF.java
public interface ServiceIF {
public void doService();
}
以上是具体提供服务的对象的接口。下面给出其实现。
//Service.java
public class Service implements ServiceIF{
public void doService(){
System.out.println("正在执行您申请的服务!");
}
}
使用JDK中的动态代理时必须实现InvocationHandler接口,实现该接口的对象一般会封装代理模式中的目标对象,同时实现其中的invoke方法。以下是实现该接口的代码:
//DynamicProxyInvocationHandler.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyInvocationHandler implements InvocationHandler {
private Object realService = null;
public DynamicProxyInvocationHandler(Object service) {
this.realService = service;
}
public Object invoke(Object proxy, Method m, Object[] args){
Object result = null;
System.out.println("您正在申请服务" + m.getName());
try {
result = m.invoke(realService , args);
}catch(Exception ex) {
System.exit(1);
}
System.out.println("您申请的服务 " + m.getName() + " 已经完成 ");
return result;
}
}
接口中的invoke方法包含三个参数:Object、Method、Object[],同时包含一个Object类型的返回值。该方法的含义为:使用Object[]类型的参数列表args调用proxy对象的m方法,返回Object类型的对象。 在上面的实现中,在调用具体对象的方法前首先打印正在申请某个对象的某个方法,接着调用某个对象的某个方法,在调用具体对象的方法后打印申请的服务已经完成。这就是我们上面所说的为真实对象增加功能。下面是一段最后的测试代码:
//DynamicProxyTest.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String args[]) {
Service realService = new Service();
ServiceIF proxy = (ServiceIF)Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new DynamicProxyInvocationHandler(realService));
proxy.doService();
}
}
在测试中首先创建一个真实的服务对象,即代理模式中的目标对象,然后使用Proxy类的newProxyInstance静态方法,创建一个具体的代理对象,由于代理对象和被代理对象具有相同的接口ServiceIF,因此我们在该代理对象被创建后downcast为ServiceIF接口,接着调用该代理对象的服务方法。下图是执行结果图:
有兴趣可以继续研究编译器的实现、字节码的细节、以及虚拟机的实现。