OSGi - 第03章 BundleContext.getService()详解之一


先看一段典型的获取并使用服务的代码:

void logMessage(String msg) {
	LogService logService = null;

	BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
	ServiceReference ref = context.getServiceReference(LogService.class);
	if (ref != null) {
		logService = context.getService(ref);
	}

	if (logService != null) {
		try {
			logService.log(LogService.LOG_INFO, msg);
		}
		finally {
			context.ungetService(ref);
		}
	}
	else {
		System.out.println(msg);
	}
}

上面代码的作用是使用标准日志服务记录一行日志。估计你看完就晕了,我也是。。怪不得OSGi是如此的难用。。


好了,书归正传,我们看一下这段代码里的几个问题:

1、为什么要分两阶段来获取服务?

2、为什么需要ungetService?


先看第1个问题:为什么要分两阶段来获取服务?

ServiceReference ref = context.getServiceReference(LogService.class);
if (ref != null) {
	logService = context.getService(ref);
}
我的理解:这是OSGi框架中 服务动态性和性能 折衷的结果。

首先,OSGi以服务动态性为设计目标,即:允许在不停机(这儿是指JVM)的状况下,动态的更新升级服务。

要实现这一点,首先,服务客户端(比如上面的logMessage方法)应该在每次使用服务时,动态的去查询获取最新的服务对象,

而不应该持久的存储服务对象的引用----就像上面方法展示的那样。


我们知道,OSGi模块层一个核心的特性就是支持同一个类库(这儿就是Bundle)的不同版本并存,并且按需连接到需要的客户端;

从而解决所谓的DLL地狱问题。

这意味着相同包名、相同类名、但却完全不同的Class可以并存在JVM中。服务(Service)同样存在这样的问题,

即:不同Bundle可以注册具有相同服务名称(接口或者类的完全限定名)的服务。

所以,在从服务注册表中查询服务时,一件很重要的事情就是进行“”可见性过滤“”。

public boolean isAssignableTo(Bundle bundle, String className);
ServiceReference.isAssignableTo方法就用来做这种可见性检查。

由于OSGi复杂的类加载机制,这个检查是相对耗时的操作,不适合频繁操作。


所以,一般的用法是缓存ServiceReference对象,而每次在使用前再调用context.getService(ref)来获取动态服务。

如果在调用期间,ref所执行的服务卸载、或者更新了,那么原来持有的ref机成为无效(invalid),getService(ref)将返回null

服务客户端可以检查处理这种情况,来实现服务的动态性的同时兼顾到性能问题。


-----------------------------------

再看第2个问题:为什么需要ungetService?

先看看网上的典型的解释:

BundleContext.ungetService()释放服务

ungetService()的参数是服务引用,告诉框架我们不再用这个服务了。框架会记住有几个bundle在使用某个服务,如果数目为0,则提供服务的bundle停止时,可以安全地移除该服务。

ungetService()要放在finally块中!保证即使抛出异常,仍然会执行。

——注意这一步是必须的!不像serviceRegistration.unregister()

在回答这个问题前,需要做几个试验:

1、客户端Bundle B通过 Import-Package方式显式引用服务端Bundle A的Package,并持有其对象;

2、客户端Bundle B持有服务端Bundle A的一个服务对象;(但并不导入Bundle A的任何package----想象一下LogService的实现模块)

3、客户端Bundle B使用服务端Bundle A的一个服务对象,并在使用完成后ungetService;

4、对于以上情况,分别uninstall Bundle A,观察一下OSGi缓存(比如Felix的cache目录)的情况;


对于场景一,Bundle B显式依赖Bundle A的情况,uninstall Bundle A的结果,实际上BundleA并不会从cache这删除。

这点很容易理解,因为BundleB和BundleA的ClassLoader形成了静态的委托关系(更准确的术语是org.osgi.framework.wiring.BundleWire),

当这种ClassLoader之间的委托关系建立以后,Bundle的状态才会变成已解析(RESOLVED)

Bundle A 的uninstall或者是update操作,不应该导致已经Active的BundleB,重新变成解析失败状态。

设想一下,某一个bundle在线升级了,导致所有受影响的构件都重新停止、再启动,也是一件影响非常大的事情。

所以BundleA此时是被假删除的。


OSGi框架另外提供了手动同步这种情况的方法,就是:org.osgi.framework.wiring.FrameworkWiring接口上的

	void refreshBundles(Collection<Bundle> bundles, FrameworkListener... listeners);


通过refresh操作来主动刷新需要的Bundle。

这时候,BundleB首先被Stop,然后重新再解析。。当然,这时候找不到Import-Package需要的包了,从而解析失败。

对于场景二、三,无论BundleB是否使用ungetService来释放获取的Service对象,都不影响bundleA 的卸载。


仔细查看Felix的实现代码后,大概的理解是这样的:

ungetService方法是配合 ServiceFactory接口使用的。通过ServiceFactory,你可以为不同的Bundle提供不同的Service对象,

也可以为每一次请求生成不同的Service对象,或者结合HttpSession,为每一个会话分配不同的服务对象。

而且,如果Service对象是重型对象(比如持有一个未关闭的流),那么SeviceFactory中还提供了一个unget的回调方法,

用来在Service对象不再被使用时执行一些销毁资源的动作。


同时,对同一个bundle上服务的使用,框架还会维持一个引用计数,这样做的目的是一、同一个bundle在unget之前

获取的所有服务对象一定是相同的;二、当这个服务对象不再被某个bundle使用的时候,可以回调一下ServiceFactory来释放资源。


但是!!说实话,没看到这样的设计有太大的意义。一则,服务对象通常应该设计为单例对象;二则依靠客户端来调用unget

从而触发ServiceFactory释放资源是极不可靠的。三、服务对象应该是轻量级,而不应该是重量级的。


这是一个鸡肋的设计?


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`jbundle-util-osgi-wrapped-httpclient` 是一个用于在 OSGi(开放服务网关倡议)环境中使用 Apache HttpClient 的工具库。它提供了将 Apache HttpClient 封装为 OSGi 插件的功能,使得在 OSGi 容器中可以方便地使用 HttpClient 进行 HTTP 请求和处理。 Apache HttpClient 是一个功能强大的 HTTP 客户端库,用于在 Java 应用程序中进行 HTTP 通信。它提供了丰富的功能和灵活的配置选项,包括支持各种 HTTP 方法、处理认证和代理、处理 Cookie、支持连接池等。 使用 `jbundle-util-osgi-wrapped-httpclient` 可以进行以下操作: 1. 封装 Apache HttpClient:`jbundle-util-osgi-wrapped-httpclient` 将 Apache HttpClient 封装为 OSGi 插件,使得它可以在 OSGi 容器中进行动态加载和管理。这样你就可以通过 OSGi 的方式来使用 HttpClient,而无需手动处理其依赖和生命周期管理。 2. OSGi 配置和服务:工具库提供了一些实用方法和类,用于在 OSGi 环境中配置和使用 HttpClient。它提供了 OSGi 配置管理器和服务注册的支持,使得你可以方便地配置和获取 HttpClient 实例。 3. HTTP 请求和响应处理:`jbundle-util-osgi-wrapped-httpclient` 提供了一些工具类和方法,用于发送 HTTP 请求和处理响应。你可以使用这些工具来构建和发送 HTTP 请求,并处理服务器返回的响应数据。 需要注意的是,`jbundle-util-osgi-wrapped-httpclient` 是一个针对 OSGi 环境的工具库,主要用于在 OSGi 容器中使用 Apache HttpClient。如果你不使用 OSGi,可以直接使用 Apache HttpClient 的原生库。 希望这个解释对你有帮助!如果你有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值