對于采用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帶給你的更多的就是享受,而非痛苦了。