OSGi松耦合最佳实践

时间:2011-02-09 14:50:28

标签: java design-patterns osgi

我想知道什么被认为是将应用程序代码与框架代码分离的最佳实践或模式,特别是关于OSGi。

我将使用example from the Felix SCR pages

示例服务是Comparator

package sample.service;
import java.util.Comparator;
public class SampleComparator implements Comparator
{
    public int compare( Object o1, Object o2 )
    {
        return o1.equals( o2 ) ? 0 : -1;
    }
}

上面的代码不包含框架管道,它集中和简洁。在使用OSGi时,将其提供给应用程序涉及将其注册到服务注册表。如链接的Felix页面所述,一种方法是使用服务组件运行时。

// OSGI-INF/sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<component name="sample.component" immediate="true">
  <implementation class="sample.service.SampleComparator" />
  <property name="service.description" value="Sample Comparator Service" />
  <property name="service.vendor" value="Apache Software Foundation" />
  <service>
    <provide interface="java.util.Comparator" />
  </service>
</component>

Service-Component: OSGI-INF/sample.xml

一切都很好,可爱,我的服务实现根本没有与OSGI的耦合。

现在我想使用该服务......

package sample.consumer;
import java.util.Comparator;
public class Consumer {
    public void doCompare(Object o1, Object o2) {
        Comparator c = ...;
    }
}

使用SCR查找策略我需要添加仅框架方法:

protected void activate(ComponentContext context) {
    Comparator c = ( Comparator ) context.locateService( "sample.component" );
}

使用SCR事件策略我还需要添加仅框架方法:

protected void bindComparator(Comparator c) {
    this.c = c;
}

protected void unbindComparator(Comparator c) {
    this.c = null;
}

两者都不是非常繁琐,但我认为很可能你最终会在类中重复使用相当数量的这类代码,这会使得过滤时产生更多噪音。

我可以看到的一个可能的解决方案是使用OSGi特定类来通过更传统的方式和框架在消费者之间进行调解。

package sample.internal;
public class OsgiDependencyInjector {
    private Consumer consumer;
    protected void bindComparator(Comparator c) {
        this.consumer.setComparator(c);
    }

    protected void unbindComparator(Comparator c) {
        this.consumer.setComparator(null);
    }
}

虽然我不确定你如何在SCR配置中安排这个。

还有org.apache.felix.scr.annotations,但这意味着只有在使用maven-scr-plugin构建时它才会起作用。真的不是那么糟糕,而且,AFAICT,它们不会产生运行时影响。

所以,现在你已经阅读了所有这些内容,你有什么建议消费OSGi提供服务的最好方法,而不用框架代码“污染”应用程序代码?

3 个答案:

答案 0 :(得分:3)

1)我认为绑定方法不会污染您的代码,它们只是bean设置器(您也可以将它们称为setXXX更传统)。您还需要进行单元测试。

2)如果您使用bnd(使用maven,ant,bndtools,eclipse插件等),那么您也可以使用bnd注释。然后bnd将自动为你创建(总是可怕的)xml。

package sample.service;
import java.util.Comparator;
import aQute.bnd.annotations.component.*;

@Component
public class SampleComparator implements Comparator {
    public int compare( Object o1, Object o2 ) {
        return o1.equals( o2 ) ? 0 : -1;
    }
}

@Component
class Consumer {
    Comparator comparator;

    public void doCompare( Object o1, Object o2 ) {
      if ( comparator.compare(o1,o2) ) 
        ....
    }

    @Reference
    protected setComparator( Comparator c ) {
       comparator = c;
    }
}

在你的清单中,只需添加:

Service-Component: *

这将由bnd接收。因此,您的域代码中没有OSGi代码。您可能会感到困惑,没有未设置的方法,但bnd的默认值是静态绑定。因此,在激活之前调用set方法,并在调用unset之前取消激活。只要你的Consumer对象也是μ服务,你就是安全的。有关μservices的更多信息,请查看bndtoolsbnd主页和我的blogs

PS。您的样本是无效代码,因为如果o1!= o2,o1将回答大于和小于o2的两者,比较器合同不允许这样做,并且会使分类不稳定。

答案 1 :(得分:2)

我会告诉你我们在项目中是如何做到的。作为一个OSGi容器,我们使用的是Fuse ESB,尽管可以找到Apache Karaf中的某个地方。为了不污染我们的代码,我们使用Spring DM(http://www.springsource.org/osgi),这极大地促进了与OSGi容器的交互。它是针对Equinox 3.2.x,Felix 1.0.3+和Knopflerfish 2.1.x进行测试的,作为我们持续集成过程的一部分“(最新版本)。

这种方法的优点:

  • xml文件中的所有“osgi”配置 - 代码未受污染
  • 使用OSGi容器的不同实现的能力

看起来如何?

  • 在OSGi注册表中发布服务:
  

&LT; osgi:service id =“some-id”   ref =“bean-implementation-service-to-expose”interface =“interface-of-your-service”   /&GT;

  • 从OSGi注册表导入服务:
  

&LT; osgi:reference id =“bean-id”interface =“interface-of-exposed-service”/&gt;

此外,要创建有效的OSGi包,我们使用maven-bundle-plugin。

答案 2 :(得分:1)

与aQute.bnd.annotations.component中的注释相比,felix注释的优势似乎是felix scr插件自动创建了bind和unbind方法(您可以注释私有字段)。 felix插件的缺点是它作用于Java源代码,因此不适用于用其他语言(如scala)创建的类文件。