繁体   English   中英

从静态方法解析OSGi服务实例

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM