问题描述
我正在尝试定义一个单例Osgi服务,该服务将由Eclipse RCP应用程序中的其他插件使用(共享),但是每个插件都有其自己的 version (来自本地类加载器),并且仅排名最高的版本会被注入。
(在com.test.taskmodel
包中)这样定义了服务(暂时不使用单独的接口和实现):
@Component(scope=ServiceScope.SINGLetoN,service=TaskService.class)
public final class TaskService {
public TaskService() {}
@Activate
void activate(BundleContext bundleContext) {
//activate
}
public Result doStuff() {
return null;
}
}
当我使用bnd构建它时,生成的jar清单包含Service-Component: Osgi-INF/com.test.TaskService.xml
,而xml是:
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0" name="com.test.TaskService" activate="activate" deactivate="deactivate">
<service scope="singleton">
<provide interface="com.test.TaskService"/>
</service>
<implementation class="com.test.TaskService"/>
</scr:component>
有三个使用该服务的Eclipse RCP插件:taskbrowser
,taskfilter
和taskdetail
。目前,它们几乎都是相同的,每个都只有一个这样的部分:
public class TaskbrowserPart {
@Inject @Service @Optional private TaskService taskService;
@postconstruct
public void createControls(Composite parent) {
if (taskService == null) {
System.out.println("Taskbrowser TaskService null");
} else {
System.out.println("Taskbrowser TaskService non-null");
}
}
}
运行应用程序时,我得到以下输出:
Taskbrowser TaskService non-null
TaskFilter TaskService null
TaskDetail TaskService null
在osgi控制台中调用service
命令将显示四个已注册的服务,只有第一个已被所有树插件使用:
{com.test.taskmodel.TaskService}={service.id=63,service.bundleid=155,service.scope=bundle,component.name=com.test.taskmodel.TaskService,component.id=34}
"Registered by bundle:" com.test.taskbrowser [155]
"Bundles using service"
com.test.taskfilter [165]
com.test.taskdetail [158]
com.test.taskbrowser [155]
{com.test.taskmodel.TaskService}={service.id=65,service.bundleid=158,component.id=36}
"Registered by bundle:" com.test.taskdetail [158]
"No bundles using service."
{com.test.taskmodel.TaskService}={service.id=66,service.bundleid=159,component.id=37}
"Registered by bundle:" com.test.taskmodel [159]
"No bundles using service."
{com.test.taskmodel.TaskService}={service.id=87,service.bundleid=165,component.id=39}
"Registered by bundle:" com.test.taskfilter [165]
"No bundles using service."
当我用service.scope=bundle
定义服务时,为什么scope="singleton"
有多个服务实例?如何以仅存在一个服务实例且所有插件都使用它的方式定义它?为什么Eclipse为什么尝试仅注入第一个服务,然后又在taskfilter
和taskdetail
插件中注入失败,因为它们使用了不同的类加载器,因此服务类型不匹配?为什么在选择服务时不考虑类加载器,而仅在注入服务时考虑?在那种情况下,我至少在每个插件中都有一个单独的实例,这不是我想要的,但至少是我可以使用的东西。感谢您的任何帮助,谢谢。
解决方法
您要完成的实际上是声明式服务的默认行为。您不需要指定ServiceScope.SINGLETON属性,也不需要在此处使用其他@Service注释(尽管这两个都不会引起问题)。
这是否在使用工具创建DS XML文件的Eclipse IDE本身中发生?我注意到您在问题中使用Bnd进行引用。您看到周围有旧的XML文件或服务组件条目吗?还是OSGi缓存中可能有过时的数据?
假设Service-Component条目仅在TaskModel捆绑包中,那么您真的不应该看到TaskBrowser,TaskDetail和TaskFilter捆绑包创建TaskService。
,您似乎在所有捆绑软件中都声明了该服务,从而使每个捆绑软件都有自己的TaskService实现。它们之间是不兼容的,因此当第一个实例返回给非所有者时,它是来自不同的类加载器。
@Patrick Paulin在回答中提到,您可以在服务打印输出中看到这一点
"Registered by bundle:" com.test.taskbrowser [155]
"Registered by bundle:" com.test.taskdetail [158]
"Registered by bundle:" com.test.taskmodel [159]
"Registered by bundle:" com.test.taskfilter [165]
已注册,表示该类定义是“提交”的(不是它创建了实例)。
当您使用广泛的选择器(例如
)定义jar内容时,它可能会在Bnd中发生-private-package: com.test.*
Private-Package: com.test.*
Export-Package: com.test.*
导致com.test.taskservice随处可见。 DS会找到您的声明,并在每个捆绑包中创建相同的scr:component
xml,彼此竞争。
请参见https://bnd.bndtools.org/heads/private_package.html
根据在Export-Package和Private-Package标头中给出的指令,遍历类路径上的包并将它们复制到输出中。