使用 OSGi 将组件注入 POJO

Posted

技术标签:

【中文标题】使用 OSGi 将组件注入 POJO【英文标题】:Injecting components into a POJO using OSGi 【发布时间】:2013-05-27 04:41:32 【问题描述】:

我是 OSGi 的新手,我有兴趣将我的一些 jar 改装成 OSGi 包。 但是我不想为任何 osgi 特定的库引入额外的依赖项。

因为这样的注释是不可能的,对捆绑上下文的编程调用也是不可能的。

我发现声明式服务与我的要求几乎匹配,这允许我在不影响依赖关系的情况下公开较低级别的捆绑包,但是在较高级别(我实际上需要使用服务)我仍然有点卡住。

我知道组件 xml 可用于声明服务的实现(我已经将其用于我的较低级别的 jar),也可以将服务实例注入特定的 POJO。

现在我的问题是:如何访问已注入服务的 osgi 管理的 POJO?在不引入新依赖项的情况下是否有可能,或者我必须以编程方式进行?

如果是后者,有人可以指点我一些代码的方向吗?换句话说,bundleContext.getServiceReference() 的组件等效项?

更新

澄清一下,如果你学习本教程的第五部分:http://www.vogella.com/articles/OSGiServices/article.html

他声明了一个component.xml 文件,该文件使用引用绑定将服务注入到QuoteConsumer 对象中。 太好了,现在我如何获得一个注入了必要服务的 QuoteConsumer 实例,我不能很好地执行“new QuoteConsumer()”,对吗?

更新2

目前我正在将 osgi 创建的实例注册为可以请求的静态变量,我认为这不是最好的方法,尤其是因为我无法将构造函数设置为私有。 (后者至少会产生一个真正的单例)

基本上工厂类有:

private void activate() 
    instance = this;

UPDATE3

工厂的完整示例:

public class Factory 

    private static Factory instance;

    public static Factory getInstance() 
        if (instance == null)
            instance = new Factory();
        return instance;
    

    private MyInterface implementation;

    public void setMyInterface(MyInterface implementation) 
        this.implementation = implementation;
    

    public void unsetMyInterface(MyInterface implementation) 
        implementation = null;
    

    public MyInterface getMyInterface() 
        if (implementation == null) 
            ServiceLoader<MyInterface> serviceLoader = ServiceLoader.load(MyInterface.class);
            Iterator<MyInterface> iterator = serviceLoader.iterator();
            if (iterator.hasNext())
                implementation = iterator.next();
            else
                implementation = new MyInterfaceStub();
        
        return implementation;
    

    @SuppressWarnings("unused")
    private void activate() 
        instance = this;
    
    @SuppressWarnings("unused")
    private void deactivate() 
        instance = null;
    

任何客户端代码都可以这样做:

Factory.getInstance().getMyInterface();

并接收 OSGi 加载的服务,加载一个或一个 stub 的 SPI。 如有必要,您仍然可以手动设置服务实例。

UPDATE4

进一步澄清:此模式不适用于从一开始就设计为在 OSGi 容器中运行的应用程序,而是适用于必须在任何地方运行的低级库,即使在 OSGi 容器上也不能假设所有消费者实际上都在使用 OSGi。

【问题讨论】:

【参考方案1】:

你听起来很困惑...... :-) 服务是静态工厂的替代品,所以你的工厂不应该存在。

DS 的整体理念是针对每个组件:

    等到它的依赖得到满足 创建实例 将实例绑定到其依赖项 在实例上调用激活 将实例注册为服务

因此,每当您获得由 DS 管理的服务时,它就已经被注入(绑定)了它的依赖项。因此,只要您保持服务依赖项,就永远不需要静态工厂……服务的整个想法是您没有静态工厂,只能使用(注入的)实例。 OSGi 最好的部分之一是您很少与工厂合作。

关于不使用注释的要求的评论。 OSGi 注释只是类时间,它们不会创建运行时依赖项。我强烈建议使用它们,因为它们使服务像一个类一样轻量级,并且与 XML 相比是类型安全的。

使用注释而不使代码混乱的一个技巧是创建扩展您希望成为 OSGi 组件的实现类,并在此类上添加注释。

【讨论】:

感谢您的反馈。工厂实现背后的原因是模块应该在任何环境中运行。所以总的来说,我所有的模块都“期望”托管注入,但如果没有发生这种情况,应该回退到工厂模式。但是让工厂支持 OSGi 可能有点傻,它可以只使用 SPI :) 除非您正在改造现有的工厂,该工厂由不支持 OSGi 的模块使用? 嗯,在 OSGi 世界中,事情看起来非常简单明了:每个对象都是一个组件。因此,在非 OSGi 世界中,您需要一些 DI 引擎来进行注入。【参考方案2】:

我会说你在正确的轨道上。如果方便,您可以使用静态字段。

重要的是您让其余代码处理 QuoteConsumer 的出现和消失。因此,在您的激活器中放入代码以在 QuoteConsumer 可用时执行您需要执行的操作(在某个字段中注册它,调用一些初始化代码,我不知道)并放入您需要指示的停用代码QuoteConsumer 不再可用。

【讨论】:

代码目前基本允许三种操作模式:OSGi、SPI和手动设置,无需引入新的依赖。因此,如果 OSGi 实例被删除(或从不存在),它将回退到 SPI,如果这不起作用,您有责任自己管理它。我将使用示例代码更新原始问题【参考方案3】:

要访问服务,您需要声明另一个组件对它的引用:

@Reference
public void setFoo(Foo foo) 
    this.foo = foo;

您可能会发现Bndtools tutorial 将有助于澄清这些概念。

【讨论】:

以上是关于使用 OSGi 将组件注入 POJO的主要内容,如果未能解决你的问题,请参考以下文章

OSGI 环境中的依赖注入

未注入 OSGI 服务 JPA PersistenceContext

如何使用 openFromComponent 将snackBarRef 注入组件

如何使用 Angular 中的依赖注入将属性指令实例传递给嵌套组件

Eclipse OSGi 包需要另一个包的片段

Blazor University (45)依赖注入 —— 将依赖项注入 Blazor 组件