思路

OSGi每个模块都有自己独立的classpath。如何实现这一点呢?是因为OSGi采取了不同的类加载机制:

  • OSGi为每个bundle提供一个类加载器,该加载器能够看到bundle Jar文件内部的类和资源;
  • 为了让bundle能互相协作,可以基于依赖关系,从一个bundle类加载器委托到另一个bundle类加载器。
 

Java和J2EE的类加载模型都是层次化的,只能委托给上一层类加载器;

而OSGi类加载模型则是网络图状的,可以在bundle间互相委托。——这样更合理,因为bundle间的依赖关系并不是层次化的。

例如bundleA、B都依赖于bundleC,当他们访问bundleC中的类时,就会委托给bundleC的类加载器,由它来查找类;如果它发现还要依赖bundleE中的类,就会再委托给bundleE的类加载器。

优点

  • 找不到类时的错误提示更友好。假如bundleE不存在,则bundleC就不会被解析成功,会有错误消息提示为何未能解析;而不是报错ClassNotFoundException或NoClassDefFoundError。
  • 效率更高。在标准Java类加载模型中,总是会在classpath那一长串列表中进行查找;而OSGi类加载器能立即知道去哪里找类。

类加载步骤

 

 
 

Step 1: 检查是否java.*,或者在bootdelegation中定义

当bundle类加载器需要加载一个类时,首先检查包名是否以java.*开头,或者是否在一个特定的配置文件(org.osgi.framework.bootdelegation)中定义。如果是,则bundle类加载器立即委托给父类加载器(通常是Application类加载器)。

这么做有两个原因:

  • 唯一能够定义java.*包的类加载器是bootstrap类加载器,这个规则是JVM要求的。如果OSGI bundle类加载器试图加载这种类,则会抛Security Exception。
  • 一些JVM错误地假设父加载器委托永远会发生,内部VM类就能够通过任何类加载器找到特定的其他内部类。所以OSGi提供了org.osgi.framework.bootdelegation属性,允许对特定的包(即那些内部VM类)使用父加载器委托

Step 2: 检查是否在Import-Package中声明

检查是否在Import-Package中声明。如果是,则找到导出包的bundle,将类加载请求委托给该bundle的类加载器。如此往复。

Step 3: 检查是否在Require-Bundle中声明

检查是否在Require-Bundle中声明。如果是,则将类加载请求委托给required bundle的类加载器。

Step 4: 检查是否bundle内部类

检查是否是该bundle内部的类,即当前JAR文件中的类。

Step5:  检查fragment

搜索可能附加在当前bundle上的fragment中的内部类。

什么是fragment?

Fragment bundle是OSGi 4引入的概念,它是一种不完整的bundle,必须要附加到一个host bundle上才能工作;fragment能够为host bundle添加类或资源,在运行时,fragment中的类会合并到host bundle的内部classpath中。

fragment有什么作用?

【场景1】bundle中有针对特定平台的代码

假设bundle对不同平台的实现方式稍有不同,Windows和Linux下代码有不同之处,即bundle中有针对特定平台的代码。

我们应该为每个平台提供不同的bundle吗?——显然不能,因为那会造成代码重复。

或者将共同代码放到bundle A中,Windows特定的那部分代码放到bundle Pwin中,Linux特定的那部分代码放到bundle Plinux中。——有问题:Pwin肯定要依赖A中某些包,我们就必须在A中导出这些包,如果只有Pwin用到这些包 岂不破坏封装性。

最好的解决方法是把Pwin作为fragment,附加到A中。这样Pwin就能看到A中的所有包,A也能看到Pwin的所有包。

【场景2】针对不同国家用户提供不同的i18n

GUI程序通常会通过properties文件定义i18n信息,可以将不同的i18n存到不同的fragment中。运行时用户只需要下载host bundle以及特定的i18n fragment即可,不需要把其他国家的i18n也下载下来。

Step6: 动态类加载

//TODO

完整的类加载流程图如下:

The class search algorithm as described always attempts to load classes from another bundle in preference to classes that may be on the classpath of the present bundle. This may seem counterintuitive at first, but in fact it makes a lot of sense, and it fits perfectly with the conventional Java approach.
A traditional Java class loader always first delegates to its parent before attempting to define a class itself. This helps to ensure that classes are loaded as few times as possible, by favouring already-defined copies of a class over redefining it in a lower-level class loader. The opposite approach would result in many copies of the same class, which would be considered incompatible because they were defined by different class loaders.
Back in OSGi, where the class loaders are arranged in a graph rather than a tree, the same principle of minimising class loading translates to making every effort to find a class from a bundle’s imports rather than loading it from the internal bundle classpath.
 

OSGi类加载流程的更多相关文章

  1. OSGi类加载问题

    项目中遇到的JVM难点 ——启动OSGi容器时,出现永久代内存不够.内存泄露 ——OSGi找不到类路径问题. ——线程死锁问题.   问题一:OSGi类内存问题         其次,从内存用量来看, ...

  2. JVM,Tomcat与OSGi类加载机制比较

    首先一个思维导图来看下Tomcat的类加载机制和JVM类加载机制的过程 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加载. 比如JVM启动时,会通过不同的类加载器加载 ...

  3. 并行类加载与OSGI类加载

    这回来分析一下OSGI的类加载机制. 先说一下OSGI能解决什么问题吧. 记得在上家公司的时候,经常参与上线.上线一般都是增加了一些功能或者修改了一些功能,然后将所有的代码重新部署.过程中要将之前的服 ...

  4. Android类加载流程

    背景 由于前前前阵子写了个壳,得去了解类的加载流程,当时记了一些潦草的笔记.这几天把这些东西简单梳理了一下,本文分析的代码基于Android8.1.0源码. 流程分析 从loadClass开始,我们来 ...

  5. JVM类加载流程

    1.加载 a.装载类的第一个阶段 b.取得类的二进制流 c.转为方法区数据结构 d.在Java堆中生成对应的java.lang.Class对象 2.链接 a.验证(保证Class流的格式是正确的) 文 ...

  6. 深入理解JVM(六)——类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

  7. JVM学习--(六)类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

  8. 深入理解JVM一类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

  9. 深入理解JVM虚拟机6:深入理解JVM类加载机制

    深入理解JVM类加载机制 简述:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 下面我们具体 ...

随机推荐

  1. 如何在html显示当前时间

    下边那个是一直快速跳转的 <!doctype html>  <html>  <head>  <meta charset="utf-8"&g ...

  2. SQL优化(面试题)

    因为现在面试经常需要问的需要SQL优化,问的具体操作步骤时候的常见做法,所以网上总结这些操作步骤: SQL优化的具体操作: 1.在表中建立索引,优先考虑where.group by使用到的字段. 2. ...

  3. 网络安装Centos x64 6.10

    1.下载老毛桃PE最新增强版本,然后生成一个可启动U盘. 2.在U盘或移动硬盘中创建一个目录 MYEXT,然后把centos的安装iso放到里面. 3.引导选择从外置ISO进行安装. https:// ...

  4. [原创]基于Zybo SDIO WiFi模块调试

    采用的是RTL8189 SDIO 模块,介绍如下 The Realtek RTL8189ES-VB-CG is a highly integrated single-chip 802.11n Wire ...

  5. “浅入浅出”函数防抖(debounce)与节流(throttle)

    函数防抖与节流是日常开发中经常用到的技巧,也是前端面试中的常客,但是发现自己工作一年多了,要么直接复用已有的代码或工具,要么抄袭<JS高级程序设计>书中所述"函数节流" ...

  6. Debian+Django+uWsgi+nginx+mysql+celery

    下载系统各种依赖 nano /etc/apt/sources.list 在Debian中使用apt-get安装软件包时经常会提示让你插入netinst的光盘: Media change: please ...

  7. 基于docker实现哨兵集群部署

    简单dockerfile文件,用于演示sentinel哨兵故障转移FROM centos:latest MAINTAINER BIXIAOYU RUN groupadd -r redis && ...

  8. Linux NFS服务器的安装与配置方法(图文详解)

    这篇文章主要介绍了Linux NFS服务器的安装与配置方法(图文详解),需要的朋友可以参考下(http://xb.xcjl0834.com) 一.NFS服务简介 NFS 是Network File S ...

  9. 使用Tornado异步接入第三方(支付宝)支付

    目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...

  10. notes for python简明学习教程(1)

    print总是以(\n)作为结尾,不换行可以指定一个空 end='' 字符串前面+r, 原始字符串 \ 显示行连接 input()函数以字符串的形式 返回键入的内容 函数参数, 有默认值的形参要放在形 ...