Equinox加载Bundle Class的实现

简介:

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);
  •   从以上这个代码片段,可以看到,Equinox将java.开头的类转交给了parent classloader去加载,这也意味着没必要在系统中提供对外export java.开头的package。

      如果不是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/

    目录
    相关文章
    |
    Java API 数据处理
    使用Java内存映射(Memory-Mapped Files)处理大文件
    NIO中的内存映射 (1)什么是内存映射文件 内存映射文件,是由一个文件到一块内存的映射,可以理解为将一个文件映射到进程地址,然后可以通过操作内存来访问文件数据。说白了就是使用虚拟内存将磁盘的文件数据加载到虚拟内存的内存页,然后就可以直接操作内存页数据。
    8154 0
    |
    Java Maven
    解决遇到的问题idea maven无法加载导入子模块的依赖包
    遇到问题idea maven无法加载导入子模块的依赖包
    1371 0
    解决遇到的问题idea maven无法加载导入子模块的依赖包
    |
    2天前
    |
    云安全 数据采集 人工智能
    古茗联名引爆全网,阿里云三层防护助力对抗黑产
    阿里云三层校验+风险识别,为古茗每一杯奶茶保驾护航!
    古茗联名引爆全网,阿里云三层防护助力对抗黑产
    |
    6天前
    |
    人工智能 中间件 API
    AutoGen for .NET - 架构学习指南
    《AutoGen for .NET 架构学习指南》系统解析微软多智能体框架,涵盖新旧双架构、核心设计、技术栈与实战路径,助你从入门到精通,构建分布式AI协同系统。
    302 142
    |
    6天前
    |
    Kubernetes 算法 Go
    Kubeflow-Katib-架构学习指南
    本指南带你深入 Kubeflow 核心组件 Katib,一个 Kubernetes 原生的自动化机器学习系统。从架构解析、代码结构到技能清单与学习路径,助你由浅入深掌握超参数调优与神经架构搜索,实现从使用到贡献的进阶之旅。
    281 139
    |
    2天前
    |
    存储 机器学习/深度学习 人工智能
    大模型微调技术:LoRA原理与实践
    本文深入解析大语言模型微调中的关键技术——低秩自适应(LoRA)。通过分析全参数微调的计算瓶颈,详细阐述LoRA的数学原理、实现机制和优势特点。文章包含完整的PyTorch实现代码、性能对比实验以及实际应用场景,为开发者提供高效微调大模型的实践指南。
    361 0
    |
    3天前
    |
    传感器 人工智能 算法
    数字孪生智慧水务系统,三维立体平台,沃思智能
    智慧水务系统融合物联网、数字孪生与AI技术,实现供水全流程智能监测、预测性维护与动态优化。通过实时数据采集与三维建模,提升漏损控制、节能降耗与应急响应能力,推动水务管理从经验驱动迈向数据驱动,助力城市水资源精细化、可持续化管理。
    264 142
    |
    1天前
    |
    存储 人工智能 Java
    AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践
    本文介绍RAG(检索增强生成)技术,结合Spring AI与本地及云知识库实现学术分析AI应用,利用阿里云Qwen-Plus模型提升回答准确性与可信度。
    190 90
    AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践