如何select 一个合适的服务实现?
How to select an appropriate service implementation?
我创建了一个具有多个实现的服务,如下所示:
@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
接受参数的实现!)。服务的消费者需要能够 select 基于 属性 的适当实现 - 为简单起见,我们可以假设我使用的是实现的名称。
现有的非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);
}
是否有更好的方法来创建一个 "factory" returns "services",但不需要消费者处理 BundleContexts 等?
我发现了一些不太有效的方法:
@Reference(target = "ham")
(我知道语法错误)允许我 select 基于 属性 的实现,但仅限于编译时。
- A
ServiceFactory
允许不同的 bundle 获得服务的不同实例,但不指定实现。
服务的想法是消费者不应该 select 因为这使得消费者非常不可重用。如果将该控件移至消费者,它通常会使系统变得脆弱。消费者应使用已注册的任何内容。
一个反模式是您试图隐藏服务的实现,但随后需要在您的消费者中进行特定的实现。如果这是您的用例,您的实现是 public 并使其成为自己的服务类型。当然也可以注册成普通的MyService类型,你可以注册多个类型。
如果您有外部 select离子标准,例如一台打印机,并且您希望用户决定,然后您只需获取所有内容并将它们显示给用户。使用服务 ID,您可以使用正确的服务 ID。
如果您需要配置应该使用哪个服务,那么您应该使用 @Reference(target="(selection.criterium=foo)")
。您可以使用 Configuration Admin 覆盖此 selection。 targets 中对此进行了解释。您基本上设置了一个 属性,其中引用的名称后跟 .target
到所需的过滤器。
我创建了一个具有多个实现的服务,如下所示:
@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
接受参数的实现!)。服务的消费者需要能够 select 基于 属性 的适当实现 - 为简单起见,我们可以假设我使用的是实现的名称。
现有的非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);
}
是否有更好的方法来创建一个 "factory" returns "services",但不需要消费者处理 BundleContexts 等?
我发现了一些不太有效的方法:
@Reference(target = "ham")
(我知道语法错误)允许我 select 基于 属性 的实现,但仅限于编译时。- A
ServiceFactory
允许不同的 bundle 获得服务的不同实例,但不指定实现。
服务的想法是消费者不应该 select 因为这使得消费者非常不可重用。如果将该控件移至消费者,它通常会使系统变得脆弱。消费者应使用已注册的任何内容。
一个反模式是您试图隐藏服务的实现,但随后需要在您的消费者中进行特定的实现。如果这是您的用例,您的实现是 public 并使其成为自己的服务类型。当然也可以注册成普通的MyService类型,你可以注册多个类型。
如果您有外部 select离子标准,例如一台打印机,并且您希望用户决定,然后您只需获取所有内容并将它们显示给用户。使用服务 ID,您可以使用正确的服务 ID。
如果您需要配置应该使用哪个服务,那么您应该使用 @Reference(target="(selection.criterium=foo)")
。您可以使用 Configuration Admin 覆盖此 selection。 targets 中对此进行了解释。您基本上设置了一个 属性,其中引用的名称后跟 .target
到所需的过滤器。