Hibernate 添加了对 OSGi 的支持

  • Alex Blewitt
  • 张卫滨

2013 年 7 月 16 日

话题:语言 & 开发架构

Hibernate 是流行的 Java ORM Manager,最近添加了对 OSGi 的支持,这样使得 Hibernate 可以作为单独的 Jar 使用也可以位于 OSGi 运行时中。尽管对于库来说添加对 OSGi 的支持一般只需在 MANIFEST.MF 中添加几个条目即可,但是对于要通过反射来查找类的库来说,这可能是更具有挑战性的工作。

InfoQ 联系到了 Brett Meyer 以了解他们遇到了什么困难以及特性是如何演化的,他是 Red Hat 的软件工程师并且也是 Hibernate ORM 的核心开发人员。

InfoQ: 看起来使 Hibernate 支持 OSGi 是 最近的一个 bug,但是这个请求可以追溯到2008 年。你能介绍一下这个修正是如何进行的吗?

Meyer: 在很多方面来讲,这都是社区驱动项目取得的进展。有些人的兴趣会围绕着特定的主题并提供了迭代的解决方案,这个解决方案最终落实到了最后的结果之中。在这件事上,我过去就一直主张让 Hibernate 可以在 OSGi 环境中运行。所以,在 2012 年 8 月我加入这个团队之后,这项任务就位于我的优先级列表之中了。这整合了社区的多个成员开始做一些启动的工作并引起了整体的讨论。

InfoQ: 相对于其他的 Java 库,为 Hibernate 添加 OSGi 支持为什么更为困难呢?

Meyer: 基本上来说,针对于 JPA 中 ORM 实现的每一项困难都涉及到多个 ClassLoader。扫描、解析实体、解析映射资源、索引注解、注册 / 使用扩展点实现、代理、保持状态、缓存以及其他需要考虑的因素使得这成为一项令人有些沮丧的复杂问题。

将其放到 Hibernate 的“bundle 家族”(而不是一个“超级 jar(uber-jar)”)中,并且最终还要支持动态的持久化单元 bundle,事情会变得更加糟糕。

InfoQ: Hibernate 中有没有什么新的特性能够帮助你实现对 OSGi 的支持?

Meyer: 在 Hibernate ORM 4 中,有一些新的理念显然会使得事情更为简单。多阶段启动(Multi-phased bootstrapping)能够更为容易地在加载和处理资源之前提供 ClassLoader 和集成挂钩(hook)。集成挂钩本身能够更容易得织入在持久化单元 bundle 之中的“扩展点”实现。

但是,依然有很多静态的并且类似于创可贴式的修复,这使得在当前架构中这些问题更为明显。Hibernate ORM 5 中正在进行的新元模型将会在很大程度上增加我们的支持能力。

InfoQ: Hibernate 库如何获取对 JPA 类的引用呢?

Meyer: 为了支持多个持久化单元、多个 Hibernate ORM 实例并且可能会有两者的多个版本,我们完全避免了 DynamicImport-Package。当请求 EntityManagerFactory(JPA)或 SessionFactory(原生)时,Hibernate ORM OSGi 为 Core 提供了一个自定义的 ClassLoader。这个 ClassLoader 只关心 Hibernate bundle 的实例以及”请求 bundle(requesting bundle)“——包含持久化单元的 bundle。按照通常的做法,实体可以通过持久化单元明确地列出也可以通过扫描来进行查找。我们也可以查找”扩展点“接口的实现,如 Integrator。所有的一切要求整个持久化单元(所有的映射、实体以及扩展点)位于一个 bundle 中,它需要 EMF/SF。

需要一个单元来组成的 bundle 并不理想,我们正在努力来对其进行提高。另外,需要提及的是对于多个 Hibernate ORM 实例及多版本尚未完全支持。正如前面所提到的,Hibernate ORM 4 依然是非常静态化的,这是很令人遗憾的。针对 Hibernate ORM 5 的更为动态化的工作正在进行之中。

InfoQ: 你能够重启一个依赖于 Hibernate 的 bundle 并正确的获得新的类吗?

Meyer: 在理论上,”请求 bundle“ClassLoader 架构应该允许这样做。这个功能与要求 EMF/SF 的持久化 bundle 进行关联,因此重启应该是可行的。但是,到 Hibernate ORM 5 的时候才能真正完全支持这种动态特性,尤其是涉及到优雅地清理等等。

InfoQ: 针对 OSGi 的支持,你是如何进行测试的?

Meyer: 在 Arquillian 上,我创建了一个集成 / 单元测试,用到了 Apache Felix 作为容器。这带来了许多很独特的挑战,大多数与 ClassLoading 有关。我最初希望使用 ShrinkWrap 或 Tinybundles 在运行时动态地在内存中创建持久化单元。但是,这两种方式都会导致 Hibernate ORM Core 在获得实体的时候,实体的注解丢失(用于为 ShrinkWrap 提供 InputStream 的 ClassLoader 和在 Core 读取它们的 ClassLoader 是不同的)。还有其他的问题,如测试运行时重写了”请求 bundle“(前文所述),本质上来讲是把整个容器的 ClassLoader 赋给它了。围绕着这些问题,引入了一些 hacky 风格的丑陋的修正。

我最后形成了三个独立的资源集合:test、testClientBundle 和 testResult。test集合包含了实际的 Arquillian 测试用例。testClientBundle集合包含了持久化单元和实际的“单元测试”,用到了 Hibernate OSGi 服务(同时包含 JPA 和原生方式)并校验结果。任何的失败都会设置到一个 OSGi 服务引用中,而它是通过testResult中的接口定义的。在运行的最后,用最初的测试用例来检查服务。除了解决 ClassLoader 的问题,这几乎构建了一个现实世界的运行环境。我最后发现这种中介类型的服务构建方式与 Aries JPA 测试的工作方式非常接近。在此之前,我应该首先了解一下它们……

InfoQ: 关于如何结合 OSGi 使用 Hibernate,有什么文档或样例吗?

Meyer: 作为开始,最好的地方是我们的开发人员指导。它描述了我们所支持的环境、实现细节以及警告说明。另外,还有一个教程(Tutorial)和快速起步(QuickStart)工程。对于想了解更详细讨论的人来说,可以从JIRA 上 OSGi 的顶层任务、子任务以及关联的需求开始。

在上周发布的Hibernate 4.2.3 释放版中添加了 OSGi 的支持,你觉得这项新功能如何?

原文英文链接:Hibernate adds OSGi Support

语言 & 开发架构