OSGI声明服务(DS):使用服务组件实例的好方法是什么

时间:2009-07-09 09:20:15

标签: java service osgi declarative equinox

我刚刚开始使用Equinox和Eclipse PDE的OSGI和声明服务(DS)。

我有2个套装,A和B. Bundle A公开Bundle B使用的组件。两个bundle也会再次将此服务公开给OSGI Service注册表。

到目前为止一切正常,Equinox将组件连接在一起,这意味着Bundle A和Bundle B由Equinox实现(通过调用默认构造函数),然后使用bind / unbind方法进行连线。

现在,当Equinox正在创建这些组件/服务的实例时,我想知道获取此实例的最佳方法是什么?

假设有第三类类没有被OSGI实例化:

Class WantsToUseComponentB{
public void doSomethingWithComponentB(){
 // how do I get componentB??? Something like this maybe?
 ComponentB component = (ComponentB)someComponentRegistry.getComponent(ComponentB.class.getName());
}

我现在看到以下选项:



1。使用Activator中的ServiceTracker 来获取ComponentBundleA.class.getName()的服务(我已经尝试了它并且它可以工作,但对我来说似乎有很多开销)并通过静态工厂方法使其可用

 
public class Activator{

   private static ServiceTracker componentBServiceTracker;   

   public void start(BundleContext context){

     componentBServiceTracker = new ServiceTracker(context, ComponentB.class.getName(),null);
   }

   public static ComponentB getComponentB(){
     return (ComponentB)componentBServiceTracker.getService(); 
   };

}

2。创建某种注册表,一旦调用activate()方法,每个组件都会注册。

public ComponentB{

public void bind(ComponentA componentA){
   someRegistry.registerComponent(this);
}

public ComponentB{

   public void activate(ComponentContext context){
      someRegistry.registerComponent(this);
   }

}

}

第3。使用osgi / equinox中的现有注册表哪个实例?我的意思是OSGI已经在创建实例并将它们连接在一起,所以它已经有了某些对象。但是哪里?我怎么能得到它们?

结论 WantsToUseComponentB (不是组件而不是OSGI实例化)从哪里获取ComponentB的实例?有任何模式或最佳实践吗?正如我所说,我设法在Activator中使用ServiceTracker,但我认为没有它就可以实现。

我正在寻找的东西实际上类似于Springframework的BeanContainer,我可以说像Container.getBean(ComponentA.BEAN_NAME)。但我不想使用Spring DS。

我希望这很清楚。否则我也可以发布一些源代码来更详细地解释。

由于 克里斯托弗


更新: 回答尼尔的评论:

  

感谢您针对原始版本澄清此问题,但我认为您仍需要说明为什么无法通过DS之类的东西创建第三个类。

嗯,不知道。也许有一种方法但我需要重构整个框架以基于DS,因此不再有“新的MyThirdClass(arg1,arg2)”语句。 不知道该怎么做,但我在DS中读到了有关ComponentFactories的内容。所以不要做

MyThirdClass object = new MyThirdClass(arg1, arg2);

我可以做一个

ComponentFactory myThirdClassFactory = myThirdClassServiceTracker.getService(); // returns a 

if (myThirdClassFactory != null){
  MyThirdClass object = objectFactory.newInstance();

   object.setArg1("arg1");
  object.setArg2("arg2");
}
else{
 // here I can assume that some service of ComponentA or B went away so MyThirdClass Componenent cannot be created as there are missing dependencies?

}

在撰写本文时,我不确切知道如何使用ComponentFactories,但这应该是某种伪代码:)

由于 克里斯托弗

3 个答案:

答案 0 :(得分:7)

了Christoph,

感谢您针对原始版本澄清此问题,但我认为您仍需要说明为什么无法通过DS之类的东西创建第三个类。

DS导致组件作为服务发布,因此从DS“获取”任何组件的唯一方法是通过服务注册表访问它。遗憾的是,使用较低级别的API可能难以正确使用服务注册表,因为它是动态的,因此您必须应对服务消失的可能性或者在您希望它们可用的那一刻不可用,等等。这就是DS存在的原因:它为您提供依赖服务的抽象,并根据他们引用的服务的可用性管理组件的生命周期。

如果你真的需要在不使用DS或类似东西的情况下访问服务(并且有很多类似的东西,例如Spring-DM,iPOJO,Guice / Peaberry等),那么你应该使用ServiceTracker。我同意有很多开销 - 再次,这就是DS存在的原因。

要回答您的建议no(2),不应该创建自己的服务注册表,因为服务注册表已经存在。如果您创建了一个单独的并行注册表,那么您仍然需要处理所有动态,但是您必须在两个地方而不是一个地方处理它。这同样适用于建议(3)。

我希望这会有所帮助。

此致 尼尔

更新:顺便提一下,虽然Spring有Container.getBean()后门,但是你注意到在所有Spring文档中强烈建议不要使用后门:获取Spring bean,只需创建另一个引用它的bean 。这同样适用于DS,即获取DS组件的最佳方法是创建另一个DS组件。

另请注意,在OSGi世界中,即使您使用的是Spring-DM,也没有简单的方法可以调用getBean(),因为您需要先获取Spring ApplicationContext。这本身就是一个OSGi服务,那么如何获得该服务呢?

答案 1 :(得分:1)

克里斯托弗, 不知道我是否真的了解你的问题。 每个前。 Bundle A使用DS组件提供服务:

<service>
  <provide interface="org.redview.lnf.services.IRedviewLnfSelectedService"/>

Bundle B需要使用DS组件提供此服务:

<implementation class="ekke.xyz.rcp.application.internal.XyzApplicationLnfComponent"/>

一旦Bundle A提供服务,Bundle B就会通过实现类的bind()方法“获取”它:

public class XyzApplicationLnfComponent {
public void bind(IRedviewLnfSelectedService lnfSelectedService) {
    // here it is
}
希望这会有所帮助 EKKE

答案 2 :(得分:0)

简单方法:使用Riena将DS组件注入Activator类: http://wiki.eclipse.org/Riena_Getting_Started_with_injecting_services_and_extensions

然后你可以从任何地方调用它:Activator.getDefault()。getWhateverService()