RCP 应用程序中捆绑包之间 类 的运行时可见性

Runtime visibility of classes between bundles in RCP application

总结: 我在使用我的 OSGi 捆绑包时遇到问题,看到 classes 在 运行 时间在 OSGi 环境中可用。

上下文: 我的 RCP 应用程序将对象存储到磁盘,其中涉及存储每个对象的 class 名称。这个想法是在加载时,对象将使用写入磁盘的 class 名称重新实例化。 main AppBundle 执行存储操作。在存储的对象中,有其他 bundle 提供的类型的对象,例如 Bundle1Bundle2

问题: 当我尝试根据存储的名称读取 class 信息时,虽然相同的包及其提供的 classes 在 OSGi 运行 时间,但 class 信息不是从我的 AppBundle 可见。我提供了处理存储和读取的代码示意图。

public class Storage{
    transient Object[] objs = new Object[3];

    {
        // Something link the following happens dynamically during the application
        objs[0] = new AppBundleClass();
        objs[1] = new Bundle1Class();
        objs[2] = new Bundle2Class();
    }

    // Persisted to disk
    private String[] objTypes;

    public void saveToDisk(){
        objTypes = new String[objs.length];
        for (int i = 0; i < objTypes.length; i++)
            objTypes[i] = objs[i].getClass().getCanonicalName();
    }

    public void afterLoadingFromDisk(){
        objs = new Object[objTypes.length];
        for (int i = 0; i < objs.length; i++){
            Class<?> klass = Class.forName(objTypes[i]);
            // **** Throws error above ****

            objs = loadByClass(klass);      
            // Custom method that works for classes withing the app.
        }
    }
}

AppBundleBundle1Bundle2 没有构建依赖性。这个想法是应用程序的用户可以根据需要在 运行 时间激活不同的包。

尝试的解决方案: 我怀疑这个问题与 OSGi 对每个包使用不同的 classloader 以及 AppBundle 的 classloader 无法解析 Bundle1Class 有关,因为它不是通过捆绑依赖项或包导入指定的编译时依赖项。所以,我尝试了以下方法。

Bundle1/MANIFEST.MF
   DynamicImport-Package: *
   (to allow packages from this bundle to be dynamically visible)

AppBundle/MANIFEST.MF
    Eclipse-BuddyPolicy: global
    (to allow this bundle to be able to resolve any class available in the OSGi runtime)

预期行为: 来自 Bundle1Bundle2 的 classes 现在应该对 AppBundle 可见。

实际行为: 奇怪的是,时不时地(相当随机地),这行得通。但是,大多数时候,我 运行 会陷入有关 class 未找到的错误。

您不需要 BuddyPolicy。只需确保 Bundle1 和 Bundle2 导出包并且 AppBundle 有 DynamicImport-Package: *.

请记住,这种方法仅在每个此类包仅由一个包导出时才有效。一般来说,仅通过 class 名称来识别 class 在 OSGi 中是一种有缺陷的方法。

更好的方法是让每个包处理它知道的 classes 并自己进行序列化。这也将更好地匹配模块边界。

我同意 Christian 的观点,即仅通过名称来识别 class 是问题的根源,但我会提供不同的解决方案。

在任何模块化环境中,class 名称不足以识别 class,因为同一个名称可能为多个包所知。您的应用程序当前正在保存仅附加了 class 名称的数据,因此只需将其更改为同时保存 class 名称和包 ID,其中包标识由 Bundle-SymbolicNameBundle-Version 组成。

加载时,使用Bundle-SymbolicNameBundle-Version找到匹配的Bundle对象,然后调用Bundle.loadClass(..)按名称加载class。

您不需要导出包含持久性 classes 的包或将此包导入您的持久性框架包。