OSGi enRoute – 2.2_4 – Provider 工程

在本节中你将学到什么内容

在本节中我们创建了另外一个工程,它为我们的Eval API提供了一个实现,因此我们将之称为Provider。

创建一个Provider工程

在之前的章节中,我们创建了一个API用于编写表达式计算器。我们现在需要为此服务提供一个所谓的Provider。Provider负责实现API中定义的契约,使得Consumer可以使用该服务。在Eval服务样例中,Consumer将会调用eval(String)方法,Provider必须实现该方法。在之前的章节中,我们还学习了工程名称的最后一部分定义了工程的类型;这由OSGi enRoute模板提供支持。因此,一个Provider工程必须以.provider作为名称后缀。我们还喜欢将工程名称以工作空间、服务API名称以及实现内容描述作为前缀。下面来开始一个非常简单的实现,因此对应的名称应该为:

因此让我们来创建一个以此命名的新工程。在Eclipse中,选择New/Bndtools/Bnd OSGi Project菜单。输入新的名称((com.acme.prime.eval.provider),选择OSGi enRoute模板,并且选择默认参数。

20171119_001

实现

OSGi enRoute模板已经为我们创建了一个EvalImpl.java类。它已经被设置成了一个Declarative Service (DS)服务组件,因为在其中已经添加了 @Component 注解。如果你的Component类实现了一个或多个接口,那么它将会被注册为OSGi服务。因此在本例中,我们希望实现一个Eval接口,所以我们会注册一个Eval服务。因此我们要做的第一件事是将Eval接口添加到类中:

Build Path

不走运的是这将会导致一个错误;Eclipse无法找到Eval类,因为我们尚未连接到com.acme.prime.eval.api工程。不要直接将它添加到Eclipse的Build Path之中,因为这将会导致在持续集成时由于没有Eclipse而失败。相反我们应该将依赖添加到bnd之中来使得Eclipse和持续集成都能正常运行。

Bnd的依赖是在bnd.bnd文件中进行定义的。我们可以通过Build Path编辑器来控制Build Path。双击bnd.bnd文件并选择Build选项卡。

20171119_002

在这个选项卡中不仅会展示bnd错误,还会展示当前的Build Path(OSGi enRoute API,JUnit等)。如果要添加API工程,请点击列表上方的绿色的加号(“+”)。这将会弹出一个所有仓库的清单。如你所见,最顶上的仓库是工作空间并包含了我们的API工程。

20171119_003

你可以在搜索框中输入文本来筛选清单。双击com.acme.prime.eval.api工程并单击Finish按钮。保存bnd.bnd文件,否则不会有任何效果。

你现在可以回到EvalImple类并导入Eval接口。请选择Eval名称(它会被以红色下划线标识出来)并单击Control-1。然后单击Command-1,选择Import ‘Eval’ (com.acme.prime.eval.api)。这将会完成导入,不要忘记保存!

Bug来了!还有错误!

代码

唉,我们刚处理完导入错误,但取而代之的我们得到了一个带有红色下划线的EvalImpl类。这个问题的原因在于我们需要提供eval方法,在被实现的Eval接口中定义了该方法。让我们来简单实现一下:

OK,Ok,说起来简单,但我们还是给出了很多学习内容,不过现在不是来学习解析方法的,这次至少学到了一些错误处理方法。注意,我们的程序仅能处理简单的常量加减法。

导入

现在是时候来看看我们的模块(Bundle)到底长什么样了。请双击bnd.bnd文件并选择Contents选项卡。此选项卡向我们展示了Bundle的内容:

● 私有包(Private Package)——仅在Bundle内部可用的包。其它的Bundle可以具有同名但内容不同的包。

● 导出包(Exported package)——提供给其它Bundle的包,我们必须保持对其的维护。

● 导入包(Imported package)——希望其它Bundle提供给我们的包,希望它们的供应商和我们一样保持对其的维护。

 20171119_004

如果我们使用标准的OSGi标记法来表示这些内容,则如下图所示:

20171119_005

我们可以看到这个简单的Bundle导入了 com.acme.prime.eval.api包来获得Eval接口。这对我们的用户来说是非常不友好的,因为这将强迫他们每次都下载两个Bundle。由于服务API和我们的简单实现是紧耦合的(几乎任何改变都会强迫我们的provider进行改动),我们可以通过从Provider Bundle中导出API来简化。这可以通过将右边的“Import Packages”列表中的导入包拖动到Exported Package列表中来实现。如果在列表中已经导出了 com.acme.prime.eval.api,则你应该将其移除,因为它只是一个占位符。

在你保存bnd.bnd文件以后,你将会看到这些imports已经消失。

20171119_006

导出API应该仅仅通过服务API的Provider来完成,永远不应该是服务的消费者。

注意:注意OSGi enRoute 模板将API工程设置成了无法在运行时使用的状态,因此需要Provider将它们导出。

总之,我们的Bundle现在看起来已经不同了:

20171119_007

它如何工作?

Consumer和Provider的概念容易引起混淆,大多数情况下是因为接口的实现和接口的客户端容易混淆。然而,服务API的Provider可以在服务包中同时实现接口和/或它的客户端。Provider负责提供契约的价值,而一个Consumer接收契约的价值。我们需要区分这两个角色的原因在于它们对你如何打包和版本化Bundle具有广泛的影响。

比如你从我这里买了一栋房子,在这个场景下你是房屋契约的Consumer,而我是Provider。但出人意料的是这些角色是不对称的。例如,如果在契约签署完毕之后,卖方增加了额外的一间房间,则买方将不会反对(OK,一般来说你懂的)。然而,如果卖方移除了一个房间那么买方就会很沮丧了。一个Consumer可以期望向后兼容性,但是一个Provider会与契约紧密绑定。几乎服务契约中的任何改变都会要求Provider进行更新以提供新的功能。

因此一个Consumer离契约相对较远,并且它常常在不同的服务契约中扮演Consumer角色。一个Provider通常仅提供单独的服务契约,同时在其它的服务契约中扮演Consumer角色。

因此在OSGi的最佳实践中,一个Provider应该包含其服务API代码并将它们导出。将API与Provider隔离是毫无意义的,因为在Provider及其API之间是1:1的关系,不同于Consumer从API获得向后兼容性。将API放到Bundle中更加简单,即便如此,不要将它们放到同一个工程中,因为这将会要求编译包含实现代码的JAR。编译应该永远只与仅有API的JAR相关,避免不慎依赖实现代码。

在本节中我们将服务API拖放导入到了Exported Package列表中。对于没有使用bnd的开发者来说对此可能会有些意外,因此API不是Provider工程的一部分,它是从API工程中导出来的。然而,在bnd中你可以将任何包都放到你Bundle中,它们将会在Build Path上可用。

 

打赏一下
支付宝
微信
除非注明,博客文章均为原创,转载请标明文章地址
本文地址: http://www.javafxchina.net/blog/2017/11/osgi-enroute-0202-04/
百度未收录