Equinox的设计非常经典,其在扩展方面提供了很多的支持,同样包括类加载方面的控制,除了通过标准的org.osgi.framework.bootdelegation以及equinox提供的osgi.parentClassLoader这两个属性来简单的控制类加载之外,还可通过实现ClassLoaderDelegateHook来更为灵活的控制类加载。
对Equinox代码进行分析,想让扩展的ClassLoaderDelegateHook起作用,首先要能够让这个ClassLoaderDelegateHook注册到Equinox中,Equinox在进行类加载的过程中会调用searchHooks来寻找ClassLoaderDelegateHook的实现,寻找的方法为获取bundle.framework.delegateHooks属性,而framework中的delegateHooks属性是在initialize时通过adaptor的getHookRegistry()的getClassLoaderDelegateHooks()方法来获取的,并且framework没有提供setDelegateHooks或addDelegateHook这样的方法,所以没办法直接设置,从这来看,是否意味着只需要在framework initialize之前通过adaptor的getHookRegistry来注册ClassLoaderDelegateHook就可以呢,继续看BaseAdaptor类,恩,确实通过adaptor的getHookRegistry可以来增加ClassLoaderDelegateHook,但由于启动过程是在EclipseStarter里面完成的,BaseAdaptor是在启动的代码里创建的,这样就没办法保证在framework initialize之前增加自己实现的ClassLoaderDelegateHook了,查看EclipseStarter代码,幸运的是可以通过设置osgi.adaptor属性来指定Adaptor类的名称,于是决定继承BaseAdaptor,在构造器中创建通过获取HookRegistry来增加自行实现的ClassLoaderDelegateHook类,这里还有个小问题,BaseAdaptor在构造器中创建HookRegistry后调用了其initiliaze方法,而HookRegistry在initialize方法中将其私有的一个readonly属性设置为了true,这导致的后果是无法在这之后增加ClassLoaderDelegateHook,如增加,则会抛出:IllegalStateException,错误信息为:Cannot add hooks dynamically,因此,在调用addClassLoaderDelegateHook之前必须通过反射先将HookRegistry中的readonly属性设置为false,按照上面的方法,实现的关键代码如下:
// 设置自行实现的Adaptor类名
FrameworkProperties.setProperty(EclipseStarter.PROP_ADAPTOR, CustomAdaptor.class.getName());
public class CustomAdaptor extends BaseAdaptor {
public CustomAdaptor(String[] args) {
super(args);
HookRegistry hookRegistry=getHookRegistry();
try{
Field readOnlyField=HookRegistry.class.getDeclaredField("readonly");
readOnlyField.setAccessible(true);
readOnlyField.setBoolean(hookRegistry, false);
hookRegistry.addClassLoaderDelegateHook(自行实现的ClassLoaderDelegateHook类);
}
catch(Exception e){
e.printStackTrace();
}
}
}
ClassLoaderDelegateHook类则可根据自己的需求进行实现,在这个类中可灵活控制类加载的过程,接口中控制类加载的方法说明如下:
preFindClass
在Equinox判断是否需要从boot classloader中加载后,就会尝试调用ClassLoaderDelegateHook的这个方法来加载类,如此方法抛出ClassNotFoundException,则终止类查找,因此,如果希望Hook中加载不到时继续执行Equinox的类加载的话,在Hook中就不要抛出ClassNotFoundException。
postFindClass
在Equinox已经尝试过从Bundle的import-package、require-bundle、bundle-classpath以及dynamicimport-package中加载后,会尝试调用ClassLoaderDelegateHook的这个方法来加载类。
上面的方法并不优雅,从Equinox设计了Hook来看,不可能要这么复杂才能增加一个Hook,于是继续回头看代码,不负期望,在HookRegistry的initialize方法中,会去加载配置的osgi.hook.configurators、osgi.hook.configurators.include和osgi.hook.configurators.exclude三个属性值或指定的hookconfigurators.properties文件,最后合并形成需要加载执行的HookConfigurator类,从上面的代码来看,只用在自行实现的ClassLoaderDelegateHook类上再增加HookConfigurator接口的实现,并将其注册到HookRegistry中,最后在osgi.hook.configurators中配置这个类即可,由于equinox内部已经有配置了一些HookConfigurator的,因此此处需要把equinox jar中的hookconfigurators.properties里面配置的hook.configurators也增加进去,要么就是把自己需要增加的HookConfigurator加到equinox jar的hookconfigurators.properties中,这种方法就优雅很多了,按照这种方法实现后的关键代码如下:
// 设置需要装载的HookConfigurator的实现类
FrameworkProperties.setProperty("osgi.hook.configurators", CustomClassLoaderDelegate.class.getName());
public class CustomClassLoaderDelegate implements ClassLoaderDelegateHook,HookConfigurator {
public Class postFindClass(String name, BundleClassLoader classloader,
BundleData data) throws ClassNotFoundException {
// 自行实现
}
public URL preFindResource(String name, BundleClassLoader classloader,
BundleData data) throws FileNotFoundException {
// 自行实现
}
public Enumeration preFindResources(String name, BundleClassLoader classloader,
BundleData data) throws FileNotFoundException {
// 自行实现
}
public String postFindLibrary(String arg0, BundleClassLoader arg1,
BundleData arg2) {
// 自行实现
}
public URL postFindResource(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行实现
}
public Enumeration postFindResources(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行实现
}
public Class preFindClass(String arg0, BundleClassLoader arg1,
BundleData arg2) throws ClassNotFoundException {
// 自行实现
}
public String preFindLibrary(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行实现
}
public void addHooks(HookRegistry registry) {
registry.addClassLoaderDelegateHook(this);
}
}
ps: 在《OSGi原理与最佳实践》中没来得及写上这部分,就在这篇blog上写了。