本文是“Java 9、OSGi以及模块化的未来”系列的第二篇文章。本文的第一部分请参阅{aa38aa}。
我们将会继续深入讨论OSGi与Java平台模块化系统(JPMS),后者计划将作为Java 9的一部分而发布。在{aa37aa}中,我们在整体上对比了这两个模块化系统,描述了它们各自是如何解决在模块间进行隔离这一问题的。我们深入研究了依赖功能是如何运行的,并了解了一些反射方面的问题。在第二部分中,我们将会讨论版本化、动态模块加载以及未来OSGi和JPMS实现互操作的可能性。
版本化在软件交付中是很重要的一个方面。API和实现都会发生变化,所以,当我们依赖它们的时候,实际上我们所依赖的是它们在某个时间点上的状态。所有的模块化系统都必须要解决这一现实问题……在引用制件(artifact)或依赖项时,这一般会通过在这两者上面明确声明版本来实现。
但是,并非所有的变更都具有相同的破坏性。如果我们基于1.0.0版本的某个模块来构建和测试软件的话,那么如果部署依赖的1.0.1或1.0.5版本的话,我们的软件应该也能正常运行……但是,如果我们部署这项依赖的2.0.0版本或5.2.10版本,那么我们的软件很可能就不能正常运行了。这表明,一个模块化系统需要能够理解和支持兼容性范围的功能。
OSGi一直就支持这些理念。bundle和导出包都是版本化的。导入包指定的是一个范围,这个范围通常会包含较低的边界而不包含较高的边界,比如[1.0.0, 2.0.0),它代表了从1.0.0到2.0.0的所有版本,但是不包含2.0.0版本本身。OSGi使用语义化版本(semantic versioning),完全遵循流行的{aa36aa}(尽管OSGi本身要比这个文档更早)。大致来讲,版本号中的第一部分是主版本(major),代表了在功能和API方面的破坏性变更,第二部分是次版本号(minor),表明了这是非破坏性的功能增强,而第三部分是微版本号(micro),代表了已有功能的补丁。
OSGi开发人员不需要关心或明确声明这些版本范围。就像导入功能本身一样,版本范围也是在构建的时候通过分析依赖关系自动生成的。例如,如果我们只是作为消费者来使用API包,那么我们有可能使用一个很宽泛的范围,比如[1.0.0, 2.0.0),所有的次版本和微版本都包含在内。但是,如果我们要作为提供者实现服务接口的话,那么我们必须要以更狭窄的范围来导入包,比如[1.0.0, 1.1.0),意味着所有从1.0.0到1.1.0的版本,但是不包含1.1.0版本本身。这里的区别在于支持1.0.0版本功能的提供者将不会支持1.1.0版本,因为次版本号增加的数字表明服务提供者无法自动提供新的功能。而另一方面,对于服务的消费者来说,可以很容易地使用1.1.0或1.2.0版本,它们只需忽略掉新增的功能就可以了。
来源: