如何选择合适的服务实施?

时间:2016-08-17 01:03:25

标签: java osgi

我已经创建了一个包含多个实现的服务,如下所示:

@ProviderType
public interface MyService {
    public void printMessage();
}

@Component
public class Foo implements MyService {
    public void printMessage() {
        System.out.println("foo");
    }
}

@Component
public class Bar implements MyService {
    public void printMessage() {
        System.out.println("bar");
    }
}

实际的实现显然有点复杂(我不能只在printMessage接受参数的情况下进行单个实现!)。服务的使用者需要能够基于属性选择适当的实现 - 为简单起见,我们可以假设我使用的是实现的名称。

现有的非OSGi实现使用的工厂方法是这样的:

public MyService getService(String property) {
    switch (property) {
        case "ham":
            return new Foo();
        case "spam":
            return new Bar();
    }
}

实际的实现更复杂(它基于property参数的子串匹配)。将此转换为OSGi的最简单方法(对我而言)是将此工厂方法转换为MyService的静态方法,仅返回必要的过滤字符串,并使消费者在处理繁琐的位置时BundleContext,然后获取和取消服务:

public class MyConsumer {
    private BundleContext context;

    @Activate
    public void activate(BundleContext context) {
        this.context = context;
    }

    public void doStuff() {
        Collection<ServiceReference<MyService>> refs = context.getServiceReferences(MyService.class, MyService.getService("ham"));
        ServiceReference<MyService> service = refs.iterator().next();
        context.getService(service).printMessage();
        context.ungetService(service);
    }

有没有更好的方法来创建一个返回“服务”的“工厂”,但不要求消费者处理BundleContexts等。

我发现了一些不会完全工作的方法:

  • @Reference(target = "ham")(我知道语法错误)允许我选择基于属性的实现,但仅限于编译时。
  • ServiceFactory允许不同的bundle获取服务的不同实例,但不指定实现。

1 个答案:

答案 0 :(得分:2)

服务的概念是消费者不应该选择,因为这会使消费者非常不可重复使用。如果将控制权转移到消费者手中,它通常会产生脆弱的系统。消费者应该使用注册的任何内容。

一种反模式是您尝试隐藏服务的实现,但随后需要在您的使用者中使用特定的实现。如果这是您的用例,那么您的实现是公开的,只需将其设置为自己的服务类型即可。它仍然可以注册为常见的MyService类型,当然,您可以在多种类型下注册。

如果您有一个外部选择标准,可以使用哪个标准,例如:打印机,您希望用户决定,然后您只需获取所有内容并将其显示给用户。使用服务ID,您可以使用正确的服务ID。

如果您需要配置应使用哪种服务,则应使用@Reference(target="(selection.criterium=foo)")。您可以使用Configuration Admin覆盖此选择。这在targets中有解释。您基本上设置了一个属性,其中包含引用名称,后跟.target到所需的过滤器。