假设我具有以下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
首先被调用(获得“通知”,表明运行时已注册SomeInterface
的新实例),然后调用bindEventAdmin
。这不是理想的效果。
预期行为
我想先绑定EventAdmin
实例,然后绑定SomeInterface
实例。
我该怎么做?
非常接近的问题(但不相同):Bind order of OSGi declarative services
PS:我正在尝试避免使用ServiceTraker
之类的东西。
答案 0 :(得分:2)
如果您使用bnd生成XML,则顺序基于引用名称的词法顺序。即这些引用在写入XML之前按名称进行排序。
但是,第二个引用是动态的。我不确定您是否可以依赖该命令。我可以设想,如果在SCR试图注入之前调用 1 public static void Write<T>(string filepath, System.Collections.Generic.IEnumerable<T> records, string schema, string delimiter = "\t")
{
var properties = typeof(T).GetProperties();
var classMap = new DefaultClassMap<T>();
var schemaList = schema.Split(';');
foreach (var name in schemaList)
{
if (!properties.Any(p => p.Name.ToUpper() == name.ToUpper()))
{
throw new Exception($"The given class is missing the property '{name}'");
}
var property = properties.FirstOrDefault(p => p.Name.ToUpper() == name.ToUpper());
classMap.Map(typeof(T), property).Name(property.Name);
}
var config = new CsvHelper.Configuration.Configuration { Delimiter = delimiter };
using (var writer = new System.IO.StreamWriter(filepath))
using (var csv = new CsvHelper.CsvWriter(writer, config))
{
csv.Configuration.RegisterClassMap(classMap);
csv.WriteRecords(records);
}
}
的事件管理员之前有新服务出现。
这就是说,声明式服务规范中对此进行了详细介绍。
1 在OSGi标准化它们之前在bnd中开发的原始注释中,我将其设置为MULTIDY DYNAMIC的默认值,因为当您拥有 not 时不动态是很危险的多。在标准化过程中,我似乎无法使其他成员相信MULTISTAT STATIC是一个非常糟糕的组合,因为结果不确定,每次启动都可能改变。
答案 1 :(得分:2)
虽然您 可以使用词法排序(如Peter所建议的)或通过手动排序XML元素来控制注入的顺序,但我还是建议不要依赖于此。
在您的示例中,当绑定EventAdmin
和EventAdmin
服务时,您想向SomeInterface
发送事件。进行此操作以及其他所需的初始化的理想场所是组件的Activate方法。确保在绑定 all 静态引用之后调用activate方法。在您的情况下,SomeInterface
的基数为0..n
,因此可能多次被称为零。您可以将所有实例累积到一个列表中,然后通过Activate方法对该列表进行迭代。
您甚至不必担心使List成为线程安全的或使用同步,因为SCR确保在最后一个服务绑定与Activate方法的开始之间存在“先于后预”的关系。
答案 2 :(得分:0)
只需更改标签顺序即可完成“技巧”。 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文件中声明的顺序来解析所有引用。
有趣的是,当您使用声明式服务实现您的类并且不要过多看IDE(在本例中为Eclipse)生成的xml文件时,这可能是一场噩梦,因为您希望在Windows中解决这些服务。声明 bind 和 unbind 方法的顺序相同(.java文件中的代码行顺序)。