使用 STATIC 策略绑定 OSGi 声明性服务的顺序

Bind order of OSGi declarative services using STATIC policy

假设我有下面的 OSGi 组件,它应该在每次 SomeInterface 的新实现在 运行 时间注册时发送一个事件。

为此,我将 EventAdmin 绑定到 eventAdmin 变量,然后在 bindSomeInterface 方法中使用它。

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;

@Component
public class Sender {

    private EventAdmin eventAdmin;

    @Reference
    public void bindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = eventAdmin;
    }

    public void unbindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = null;
    }

    @Reference(cardinality = ReferenceCardinality.MULTIPLE)
    public void bindSomeInterface(SomeInterface instance) {

       // var prop create here... (non relevant code)
        Event event = new Event("topic", prop);

       // it is NULL!
        eventAdmin.sendEvent(event);
    }

    public void unbindSomeInterface(SomeInterface instance) {

    }

}

已生成 xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.package.Sender">
   <reference bind="bindSomeInterface" cardinality="0..n" interface="com.package.bindSomeInterface" name="SomeInterface" policy="static" unbind="unbindSomeInterface"/>
   <reference bind="bindEventAdmin" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unbindEventAdmin"/>
   <implementation class="com.package.Sender"/>
</scr:component>

问题

bindSomeInterface 首先被调用(得到 "notification" SomeInterface 的新实例在 运行 时间注册)然后 bindEventAdmin 被调用。这不是想要的效果。

预期行为

我想先绑定 EventAdmin 个实例,然后再绑定 SomeInterface 个实例。

我该怎么做?


非常接近的问题(但不一样):Bind order of OSGi declarative services


PS:我正在努力避免 ServiceTraker 之类的东西。

只需更改标签顺序即可 "trick"。 xml 文件现在看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.package.Sender">
   <reference bind="bindEventAdmin" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unbindEventAdmin"/>
   <reference bind="bindSomeInterface" cardinality="0..n" interface="com.package.bindSomeInterface" name="SomeInterface" policy="static" unbind="unbindSomeInterface"/>
   <implementation class="com.package.Sender"/>
</scr:component>

显然框架试图按照它们在xml文件.

中声明的相同顺序解析所有引用

有趣的是,当您使用声明性服务实现 class 并且不要过多查看 IDE 生成的 xml 文件(本例中为 Eclipse)这可能是一场噩梦,因为您希望服务以与声明 bind解除绑定 方法。

如果您使用 bnd 生成 XML,则该顺序基于引用名称的词法顺序。 IE。引用在写入 XML.

之前按名称排序

但是,第二个引用是动态的。我不确定您是否可以相信订单。我可以设想,如果在 SCR 尝试注入之前调用 bindSomeInterface 的 Event Admin 之前出现新服务。 1

也就是说,声明式服务规范中概述了所有详细信息。

1 在 OSGi 标准化之前在 bnd 中开发的原始注释中,我将 MULTIPLE DYNAMIC 默认设置为 MULTIPLE DYNAMIC 因为它看起来非常危险 not 当你有多个时要动态。在标准化过程中,我似乎无法说服其他成员 MULTIPLE STATIC 是一个非常糟糕的组合,因为结果不是确定性的,每次启动都会改变。

虽然您可以使用词法排序(如 Peter 所建议的)或通过手动排序 XML 元素来控制注入顺序,但我建议不要依赖关于这个。

在您的示例中,当 EventAdminSomeInterface 服务都已绑定时,您希望向 EventAdmin 发送一个事件。执行此操作以及任何其他所需初始化的最佳位置是在组件的 activate 方法中。保证在 all 静态引用绑定后调用 activate 方法。在您的情况下, SomeInterface 的基数是 0..n 因此它可能被称为零次到多次。您可以将所有实例累积到一个列表中,并从激活方法迭代该列表。

您甚至不必担心使 List 线程安全或使用同步,因为 SCR 确保在最后一个服务绑定和激活方法的开始之间存在 "happens-before" 关系。