实现ClassLoaderDelegateHook控制Equinox的类加载

实现ClassLoaderDelegateHook控制Equinox的类加载

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上写了。

你可能感兴趣的:(实现ClassLoaderDelegateHook控制Equinox的类加载)