[OSGi]插件平台的类加载机制

作为一个插件平台,除了要解决在运行时插件的交互问题外,还需要解决一个非常重要的问题——类加载。原因在于:(1)类加载机制可以绕过默认类型加 载器按需自动加载每一个插件的所有类型;(2)插件具备独立性,即一个插件的运行不能对其它插件和插件内核平台产生影响,这使得类加载机制必须维护每一个 插件的类型;(3)每一个插件可能会引用同一个Class的不同版本,这要求类加载机制必须能够加载多个版本的同一个类并为每一个插件维护其特定版本的类 型。本文将讨论一下基于Java和.NET的插件平台的类加载机制。

 

在Java平台,每一个类型是由ClassLoader和类型全称唯一标识。ClassLoader具有父子关系的层次结构。来自于不同 ClassLoader的相同全名的类被视为不同类型。ClassLoader提供了简单、灵活且优雅的类型加载机制,在默认的ClassLoader 中,利用其进行类型加载时,它会先尝试使用父ClassLoader进行加载,如果在父ClassLoader没有加载成功,才考虑用子 ClassLoader中加载。如果请求子ClassLoader加载的类型,若在父ClassLoader中加载成功,则子ClassLoader不会 再加载,会直接从父ClassLoader中获取。这意味着,一个在父ClassLoader中加载的类型可以和所有的子ClassLoader共享,不 过,子ClassLoader间是不能够共享类型的(这种父ClassLoader向子ClassLoader的共享行为可以通过自定义 ClassLoader来改变)。考虑到Java的默认ClassLoader机制,在我看来理想状态下的插件平台的类加载机制的设计应该如下图(虽然我 不是专业的Java开发人员,但是对其ClassLoader非常感兴趣,于是好好研究了一下,个人觉得该图的ClassLoader层次设计要比 JavaPluginFramework优雅)。这个类加载机制由3层ClassLoader组成,类加载管理器必须严格控制线程执行过程中的 ClassLoader,从而实现插件独立行、多版本类型支持、插件与平台无关性。(附:Flex的类型加载类似于Java的ClassLoader,只 不过它被命名为AppDomain,估计是吸收了.NET和Java的优点。不过,在开发插件化的Flex平台时,如果采用的是分层的AppDomain 的话,很容易碰到TypeA转换成TypeA异常的问题,我把它称为Singleton Hell Issue,因为Flex底层类库使用了不少的单件类,如果一个AppDomain注册一个实例,另一个AppDomain获取并使用则出现异常,这类异 常在Flex SDK 3.0中大量存在。因此,我们在使用Java的ClassLoader和Flex的AppDomain的时候,需要谨慎并精细。)

 

 

(鉴于本文有点抽象,于是举个例子说明一下 )假设在当前线程运行到Plugin B的代码,插件管理器在执行Plugin B的代码之前已经将其类加载器设置为PluginClassLoader了,因此,线程在执行代码的过程中,如果碰到新的类型,则将从 PluginClassLoader B中加载所需的类型;同理,当执行Plugin A的代码时,其加载器使用的是PluginClassLoader A。这为插件类型空间提供了独立性,从而使得在A和B插件可以使用同一个类型的不同版本。 

 

在.NET平台中,每一个类型是由AppDomain + 程序集全名 + 类全名来唯一标识的。微软在程序集中直接声明了版本号。因此,CLR Loader允许在一个AppDomain存在多个版本的相同程序集,这意味着在同一AppDomain可能存在不同版本的类型。.NET的程序集相对于 Java的Package而言,它天生就对版本进行了标识,也就直接解决了插件平台类型加载的多版本问题。不过,遗憾的是,加载到一个AppDomain 的程序集是无法被卸载的,除非卸载整个AppDomain。为了实现插件的动态卸载,这就要求为每一个插件创建一个独立的程序集,但这又引入了 Remoting Call,极大降低了性能。我在这里提到的类加载机制,为了性能和系统简单性考虑,忽略了插件的动态卸载,仅为一个插件平台使用一个AppDomain, 并引入了一套新的类型加载机制,该类型加载机制能够按需加载程序集,其设计,部分借鉴了Java ClassLoader的思路,如下图。在此机制的TypeLoader由其加载的Assembly组成,也具备有一个层次结构,每一个插件的类型加载都 使用自身的BundleTypeLoader进行加载。BundleTypeLoader会从CoreTypeLoader、 SharedLibTypeLoader和依赖的BundleTypeLoader加载所需的类型。

 

 

本文基于.NET的TypeLoader将基于Assembly.LoadFile实现多版本程序集的加载,因此,我们需要为每一个插件管理好其相 应的程序集,在Bundle B执行过程中,它将使用BundleTypeLoader B来加载所需的类型,而这个Loader会根据需要将对应的程序集加载到当前应用域。从而实现,在插件A和B,其类型加载仅限于从插件本身依赖的程序集加 载,虽然所有的程序集都在同一个应用域,但已经被分层管理起来了。(其实原理就只有一句话“确保在插件平台上线程执行过程中能够加载到正确的类型”,比如 执行插件A的代码时,插件A依赖的类型必须在其类加载器存在。)

 

Creative Commons License

本文基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名道法自然(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏