如何正确使用OSGi getServiceReference()

时间:2010-04-28 19:16:05

标签: service osgi

我是OSGi的新手,并且遇到了几个关于OSGi服务的例子。

例如:

import org.osgi.framework.*;
import org.osgi.service.log.*;

public class MyActivator implements BundleActivator {
  public void start(BundleContext context) throws Exception {
    ServiceReference logRef = 
      context.getServiceReference(LogService.class.getName());
  }
}

我的问题是,为什么要使用

getServiceReference(LogService.class.getName())

而不是

getServiceReference("LogService")

如果使用 LogService.class.getName(),则必须导入接口。这也意味着您必须在MANIFEST.MF中导入包 org.osgi.services.log

如果你想减少依赖性来推动松散耦合,那不是完全适得其反吗?据我所知,服务的一个优点是服务使用者不必知道服务发布者。但是如果你必须导入一个特定的界面,你必须知道谁在提供它。通过仅使用像“LogService”这样的字符串,您不必知道接口是由 org.osgi.services.log.LogService 提供的。

我在这里缺少什么?

4 个答案:

答案 0 :(得分:6)

看起来你混淆了实现和界面

使用名称的实际接口(并导入接口,无论如何最终会进行)重新获得服务所围绕的接口合约。您不关心LogService的实现,但您确实关心接口。每个LogService都需要实现相同的接口,因此您使用接口来获取服务。大家知道,LogService实际上是一些其他bundle提供的SLF4J包装器。所有你看到的是界面。那是你正在寻找的松耦合。您不必为每个实现提供接口。保留接口它自己的bundle并拥有该接口的多个实现。

附注:ServiceTracker通常更容易使用,试一试!

增加了好处:使用接口获取类名避免拼写错误,过多的字符串文字,并使重构更容易。

在您获得ServiceReference之后,接下来的几行可能会涉及到:

Object logSvc = content.getService(logRef)

// What can you do with logSvc now?!? It's an object, mostly useless

// Cast to the interface ... YES! Now you need to import it!
LogSerivce logger = (LogService)logSvc;

logger.log(LogService.LOG_INFO, "Interfaces are a contract between implementation and consumer/user");

答案 1 :(得分:3)

如果你使用LogService,那么无论如何你都会加入它。如果您编写中间件,则可能通过某些XML文件或API来获取参数化名称。是的,“LogService”将非常失败,您需要使用完全限定名称:“org.osgi.service.log.LogService”。使用LogService.class.getName()模式的主要原因是在重构代码并最小化拼写错误时获得正确的重命名。下一个OSGi API很可能会:

ServiceReference<S> getServiceReference(Class<S> type)

要求提高类型安全性。

无论如何,除非你开发中间件,否则我绝不会使用这些低级API。如果你真的依赖于一个具体的类DS,那么当你将它与bnd注释(http://enroute.osgi.org/doc/217-ds.html)一起使用时,DS就会变得更加简单。

@Component
class Xyz implements SomeService {
  LogService log;

  @Reference
  void setLog( LogService log) { this.log = log; }

  public void foo() { ... someservice ... }

}

如果您开发中间件,通常会在不知道实际类的情况下通过字符串或类对象获取服务类。在这些情况下使用基于字符串的OSGi API,因为它允许我们在最后时刻之前不创建类加载器而更加懒惰。我认为12年前我们在OSGi中犯的最大错误就是不将DS概念纳入核心......: - (

答案 2 :(得分:1)

您无法使用值"LogService" 作为获取ServiceReference的类名,因为您必须使用完全限定的类名 "org.osgi.services.log.LogService"

如果以这种方式导入包: org.osgi.services.log;resolution:=optional 并且您使用ServiceTracker跟踪BundleActivator.start()方法中的服务我建议在ServiceTracker initializazion上使用"org.osgi.services.log.LogService"而不是LogService.class.getName()。在这种情况下,你不会在捆绑开始时获得NoClassDefFoundError/ClassNotFountException

答案 3 :(得分:0)

正如basszero所提到的,你应该考虑使用ServiceTracker。它相当容易使用,并且还支持更好的编程模式。您绝不能假设您过去某个时间获得的ServiceReference仍然有效。 ServiceReference指向的服务可能已经消失。 ServiceTracker将在服务注册或取消注册时自动通知您。