3

我有一个遗留的 Java 企业应用程序,它将一堆服务注册为 Spring bean 并将它们注册到 JNDI。我想将其转换为将 Spring 与 OSGi 一起使用。

以前,服务类只是包含在需要它的任何其他类的类路径中,并且有一个看起来像这样的静态方法:

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 服务。随着时间的推移,其余的应用程序也将被模块化和 OSGi 化,最终这些静态工厂方法将被完全删除。

然而,在那之前,“这是执行 OSGi 的错误方法”的评论对我来说并不是很有帮助——我知道它不是,但这甚至不是我的最终形式......

4

4 回答 4

5

您正试图将方形钉穿过一个圆孔。每个开发人员在计算机科学 101 中都知道全局变量是不好的,所以我会说“正确”周围的引号是非常错误的,因为静态是主要的全局变量。

因为 OSGi 从不依赖全局变量(java 中的静态变量),所以您可以在运行在 OSGi 之上的应用服务器上的 WAR 文件中的 OSGi 中的 OSGi 中运行 OSGi。静态是邪恶的(首先由 OSGi 的前身 ServiceSpace 的作者 Anselm Baird 提出)。开发服务模型是为了解决您遇到的问题;任何服务实现都可以通过其接口和/或属性从任何地方引用另一个服务:

 @Reference
 void setSomeService(SomeService s) {
     this.s = s;
     ...
 }

在模块化系统中,中央上帝 XML 是一种诅咒。

您的问题没有解决方案,因为您将不可避免地遇到订购问题。在 OSGi 中,不能保证服务可用(它最强大且被误解的特性之一)。您的服务提供商可以随时自由出入。因为您的静态模型不处理依赖关系,所以它会虚假地失败,因为它假定隐式排序。现在很多人都使用这些静态解决方案,因为它往往在大多数情况下都有效;然而,我认为“大部分时间”对于计算机软件来说应该不够好。如果我们对我们的产品负责,不要以为我们会这样做......

假设您使用 Eclipse 或其他具有重构功能的 IDE,那么我很难理解为什么很难更改代码库以注入此服务?

OSGi 不像大多数 Java 库那样是临时的饮食,也不是魔术酱,它是一个成熟的模块化系统,需要你的应用程序真正模块化,即它是一种生活方式的改变。尝试使用一个框架,然后违背其流程是一种策略,在我的经验中会导致很多悲痛。

于 2013-07-24T14:06:08.080 回答
2

在任何情况下,您都希望将您的服务发布为 OSGi 服务。问题只是如何在对客户端代码影响最小的情况下访问它。

一种方法可能是使用:

FrameworkUtil.getBundle(YourClass).getBundleContext(); 这允许在静态方法中访问包上下文。从那里您可以访问该服务。

另一种方法可能是使用aries jndi。它允许您使用 jndi 检索 OSGi 服务。所以这也应该有帮助。

当然,正如彼得所说,这应该只是一个临时解决方案。

于 2013-07-24T18:45:17.857 回答
1

我必须克服一个类似的问题,即一个发布的静态接口,它提供用户使用的服务,所以我不能强迫他们完全使用 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) -> DS Annotations for 1.3 中,这会生成文件

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();

也将像以前一样工作。

于 2018-10-17T14:27:00.567 回答
0

我想我已经找到了一种方法来做到这一点 - 我正在对其进行测试,但从文档来看,这种方法似乎很有希望。

我创建了一个解析器类,它负责实际从 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();
}

正如我所说,我还没有对此进行测试,所以我们将看看它是否真的有效。

于 2013-07-24T15:20:24.627 回答