[英]Resolving an OSGi service instance from a static method
我有一个遗留的Java企业应用程序,它将一堆服务注册为Spring bean并使用JNDI注册它们。 我想将其转换为使用带OSGi的Spring。
以前,服务类只包含在需要它的任何其他类的类路径中,并且有一个静态方法,如下所示:
public class SomeService {
// private fields...
public static SomeService getInstance() {
SomeService svc = null;
try {
InitialContext ctx = new InitialContext();
svc = (SomeService)ctx.lookup("java:/SomeService");
} catch (NamingException ex) {
logger.info("Exception in getNamedObject", ex);
}
return svc;
}
// Setters and getters, some of which are filled-in with Spring beans
// Other methods etc...
}
无论使用何种服务,我们都有这样的代码:
SomeService svc = SomeService.getInstance();
// or even
SomeObject results = SomeService.getInstance().getSomeObject();
现在,我意识到转换它的“正确”方法是完全删除getInstance()
并强制接口的所有用户拥有自己的实现引用,由Spring提供并在META-INF的xml文件中配置。 但是,由于系统已经投入生产,因此这种变化太大而无法立即完成。
有没有办法获取类似于上面的JNDI方法的OSGi服务实例?
更新:一些澄清
只是为了清楚我的目标 - 我知道这不是一般的好方法。 然而,这是一个正在生产中的大型企业应用程序,并且一次性改变整个事物以适应“理想的”OSGi结构只是一次性改变太大了。
我在这里要完成的是打破应用程序的一小部分,并准备好作为单独的OSGi包服务。 但是,由于应用程序的其余部分- ,“客户端代码”如果你-是不是准备好这种变化的是,我必须有一个中间步骤,它可以让我用这个代码, 无论是在旧的方式, 并作为一个OSGi服务。 随着时间的推移,其余的appilcation也将模块化和OSGi-ified,最终这些静态工厂方法将被完全删除。
然而,在那之前,“这是做错OSGi的方式”的评论对我来说并不是很有帮助 - 我知道它不是,但这甚至不是我的最终形式......
你正试图将方形钉穿过一个圆孔。 每个开发人员都在计算机科学101中学习全局变量是坏的,所以我会说“正确”的引用是非常错误的,因为静态是主要的全局变量。
因为OSGi从不依赖于全局变量(java中的静态),所以可以在OSGi中的OSGi中运行OSGi中的OSGi,该OSGi位于运行在OSGi之上的App服务器上的WAR文件中。 静态是邪恶的(首先由Anselm Baird说,它是OSGi的前身ServiceSpace的作者)。 开发服务模型是为了解决您遇到的问题; 任何服务实现都可以通过其接口和/或属性从任何地方引用其他服务:
@Reference
void setSomeService(SomeService s) {
this.s = s;
...
}
在模块化系统中,中央神XML是一种诅咒。
你的问题没有解决方案,因为你不可避免地会遇到排序问题。 在OSGi中,不保证服务可用(其最强大和最容易被误解的功能之一)。 您的服务提供商可以随时出入。 因为你的静态模型不处理依赖关系,所以它会虚假地失败,因为它假定是隐式排序。 现在很多人都做这些静态解决方案,因为它大多数时间都可以工作; 我认为“大多数时候”应该不适合计算机软件。 如果我们要对我们的产品负责,不要认为我们会这样做......
假设您使用Eclipse或其他具有重构功能的IDE,那么我很难理解为什么更改代码库以注入此服务会很困难?
OSGi不是一种临时饮食,也不像大多数Java库那样神奇,它是一个成熟的模块化系统,需要你的应用程序成为真正的模块化,即改变生活方式。 试图使用一个框架,然后反对它的流程是一种战略,导致我的经历很悲痛。
在任何情况下,您都希望将服务作为OSGi服务发布。 问题只是如何访问它,对客户端代码的影响最小。
一个方法可能是使用:
FrameworkUtil.getBundle(YourClass).getBundleContext(); 这允许以静态方法访问bundle上下文。 从那里您可以访问该服务。
另一种方法可能是使用aries jndi 。 它允许您使用jndi检索OSGi服务。 所以这也应该有所帮助。
当然,如彼得所说,这应该只是一个临时解决方案。
我必须克服类似的问题,即发布的静态接口,它提供用户使用的服务,所以我不能强迫他们完全使用OSGI。 以下带注释的代码对我来说效果很好,所以也许其他人也可以受益。
@Component(immediate=true, service=ServiceProvider.class, scope=ServiceScope.SINGLETON)
public class ServiceProvider {
private static FooService fooService;
@Reference(cardinality=ReferenceCardinality.MANDATORY)
public void setFooService(FooService fooService) {
ServiceProvider.fooService = fooService;
}
/**
* This is the existing Method which should return
* a FooService Object.
* @return The 'static' FooService
*/
public static FooService getFooService() {
return fooService;
}
}
在带有插件开发的Eclipse(4.8 Photon) - >启用了1.3的DS Annotations时,会生成该文件
OSGI-INF/your.package.name.ServiceProvider.xml
包含以下OSGI服务声明
<scr:component ...>
<service scope="singleton">
<provide interface="your.package.name.ServiceProvider"/>
</service>
<reference bind="setFooService" cardinality="1..1" interface="foo.service.package.FooService" name="FooService"/>
<implementation class="your.package.name.ServiceProvider"/>
</scr:component>
以及MANIFEST.MF中的以下条目
Service-Component: OSGI-INF/your.package.name.ServiceProvider.xml
在运行时,
setFooService(...)
首先调用方法,并按预期注入FooService对象,因此外部调用
ServiceProvider.getFooService();
也会像以前一样工作。
我想我已经找到了这样做的方法 - 我正在测试它,但从文档中这种方法看起来很有希望。
我创建了一个解析器类,它负责从OSGi实际获取服务,并通过实现BundleContextAware
来实现。 这样,我希望注入一个BundleContext
,我可以从中获取一个服务实例:
public class SomeServiceResolver implements BundleContextAware {
private BundleContext _cxt;
@Override
public void setBundleContext(BundleContext context) {
_cxt = context;
}
public SomeService resolveService() {
// TODO: Add error handling if service isn't available
ServiceReference<SomeService> svcref = _ctx.getServiceReference(SomeService.class);
SomeService svc = _ctx.getService(svcref);
return svc;
}
}
然后静态getInstance()
方法变得简单
public static SomeService getInstance() {
return new SomeServiceResolver().resolveService();
}
正如我所说,我还没有测试过,所以我们会看看它是否真的有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.