对于采用OSGi来做系统的人而言,ClassLoader的问题必然是头号需要解决的问题,如果又是个需要远程通讯的OSGi应用的话,那么反序列化的classloader问题几乎可以肯定是会碰到的,来看看在如今流行的两种序列化、反序列化协议:java/hessian中如何使用自定义的classloader。
java/hessian并不提供直接的传入ClassLoader类来改变反序列化时采用的ClassLoader,hessian采用的为使用当前线程的上下文ClassLoader来加载反序列化的类,java则采用堆栈上最近的一个ClassLoader类来加载,可以认为就是调用类所在的ClassLoader来加载,但在OSGi应用中,通常采用以上默认的行为来反序列化加载类是会出问题的,因此需要采用自定义的。
先来看看java的,Java在ObjectInputStream.readObject时最后采用的是resolveClass方法来加载类:
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String name = desc.getName();
try {
return Class.forName(name, false, latestUserDefinedLoader());
} catch (ClassNotFoundException ex) {
Class cl = (Class) primClasses.get(name);
if (cl != null) {
return cl;
} else {
throw ex;
}
}
}
惊喜的是,resolveClass是protected的,:),还好,还留下了这一手,于是毫不客气,写一个继承ObjectInputStream的子类,然后覆盖resolveClass方法即可,到这步了就可以随意用自己的ClassLoader来实现加载了。
OK,java的解决了,来看Hessian的,Hessian在加载类的时候采用了下面的代码:
if (type.startsWith("[")) {
Deserializer subDeserializer = getDeserializer(type.substring(1));
deserializer = new ArrayDeserializer(subDeserializer);
}
else {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class cl = Class.forName(type, false, loader);
deserializer = getDeserializer(cl);
} catch (Exception e) {
}
}
看过这段代码后,就明白,要替换,最明显的一种做法是替换当前线程的上下文classloader,而这通常有一定的风险,因为可能会有很多代码使用到线程的上下文classloader,另外一种可选的做法就是继承SerializerFactory,覆盖其中的getDeserializer方法,从而实现对加载类的ClassLoader的控制,这种方式风险就比较小了。
为了能实现使用自定义的classloader后可以加载到所有bundle export package中的类,在自定义classloader所在的Bundle中需要加上DynamicImport-Package,这样可以比较简单的实现。
在Equinox中,则还可以选择实现ClassLoadingHook,这样可以比较简单的实现和其他外部容器的集成以及更加自如的控制classloader,:)
在解决了classloader的问题后,通常来讲,使用OSGi带给你的更多的就是享受,而非痛苦了。