equinox在创建bundle的classloader时,首先获取bundle的classpath,然后执行createbclprevileged方法,此方法最后转交由basedata来创建classloader。
basedate创建classloader的关键代码片段为:
classloadinghook[] hooks = adaptor.gethookregistry().getclassloadinghooks();
classloader parent = adaptor.getbundleclassloaderparent();
baseclassloader cl = null;
for (int i = 0; i < hooks.length && cl == null; i++)
cl = hooks[i].createclassloader(parent, delegate, domain, this, bundleclasspath);
if (cl == null)
cl = new defaultclassloader(parent, delegate, domain, this, bundleclasspath);
return cl;
在equinox中,默认的情况下adaptor.getbundleclassloaderparent返回的为bootstrap classloader,可通过修改启动的osgi.parentclassloader 来改变这个parent classloader,
osgi.parentclassloader 的可选值有四个,分别是:
● boot:默认
● app:systemclassloader
● ext:systemclassloader的parent
● fwk:启动equinox的classloader
classloadinghook在createclassloader的时候都没有做动作,因此最后classloader都是通过创建defaultclassloader对象来构建的,其中parent参数为null,delegate参数为bundleloader实例,bundleclasspath参数为bundle的classpath。
经过以上步骤后,完成了classloader的创建,可以开始加载class了,根据上面上述,bundle的class就由defaultclassloader来完成了。
查看defaultclassloader的loadclass代码,发现真正的加载class的过程是转为调用了delegate 的findclass来完成的,delegate参数对应的为bundleloader实例,转为跟踪bundleloader的findclass方法。
bundleloader的findclass方法的代码片段:
if (checkparent && parentcl != null && name.startswith(java_package))
return parentcl.loadclass(name);
如果不是java.开头的类,则交由findclassinternal方法来完成加载。
findclassinternal方法遵循的为osgi规范中定义的class的加载顺序,不过仍然稍有改动:
1)判断是否交由parent classloader去完成加载
在启动equinox时,equinox会读取org.osgi.framework.bootdelegation属性,该属性对应配置的为需要从parent classloader中加载的package,如值配置的为*,说明所有的都从parent classloader中加载 ,如值配置的为具体的package,那么则放入bootdelegation集合;如配置的为带通配符的package,那么则放入bootdelegationstems集合。
判断时equinox首先判断是否所有的都从parent classloader中加载,如是则从parent classloader中加载;
如需要加载的类的package位于bootdelegation或bootdelegationstems集合中,那么同样从parent classloader中加载。
如不从parent classloader中加载,则进入下面的步骤。
2)尝试调用equinox提供的classloaderdelegatehook的扩展来加载
equinox对外提供了classloaderdelegatehook的接口扩展,可编写classloaderdelegatehook的实现,注册到framework中,那么当有class需要加载等动作时都会得到通知。
在默认情况下,equinox中没有classloaderdelegatehook的实现,因此继续下面的步骤。
3)判断是否在import-package中,如在则交由相应的packagesource去加载
根据bundle配置的import-package,判断目前需要加载的类是否在import-package中,如在则交由对应的packagesource进行加载,packagesource在加载时即直接交由对应的bundle的classloader去加载,如加载的类的package在import-package中,但加载后仍然没有找到class,则直接抛出classnotfoundexception,如加载到,则直接返回。
如所需要加载的类的package不在import-package中,则继续下面的步骤。
4)尝试从require-bundle中加载
尝试使用require-bundle来加载,如加载到,则直接返回,如加载不到,则继续下面的步骤。
5)尝试从当前bundle中加载
直到经过以上步骤的尝试,才尝试由当前bundle中加载,当前bundle加载的方法即从bundle-classpath或当前bundle的fragment中查找相应名称的class文件,并读取该文件进行加载,如class文件已加载,则进行缓存,再次加载时则不需要查找和解析class文件。
如从当前bundle中仍然未找到所需的类,则继续下面的步骤。
6)尝试从dynamicimport-package中加载
判断需要找的类的package是否在dynamicimport-package中,如果在,则交由相应的packagesource进行加载,如packagesource中加载不到,则抛出classnotfoundexception;如不在dynamicimport-package中,则继续下面的步骤。
7)再次尝试调用equinox提供的classloaderdelegatehook的扩展来加载
这步和第2)步相同,因此在默认情况下继续下面的步骤。
8)尝试使用eclipse的buddy机制来加载
buddy机制是eclipse的扩展,并不符合osgi规范,因此在此不做深入分析。
9)判断一定的条件,如符合则从parent classloader中加载
判断的条件为:parent classloader不为null、不从parent classloader中加载、equinox的向后兼容属性(osgi.compatibility.bootdelegation)为true以及jvm的bug class,如满足以上条件,则尝试从parent classloader中加载。
如经过以上所有步骤后,仍然未找到需要加载的class,则抛出classnotfoundexception。
从上面的代码分析中,在equinox中可以通过osgi.parentclassloader、org.osgi.framework.bootdelegation来控制从bundle classloader外来加载class,这对于集成equinox其他容器而言,非常有用,另外,还可以通过实现classloaderdelegatehook来改变class的加载。
本文出自seven的测试人生公众号最新内容请见作者的github页:http://qaseven.github.io/