将OSGi与Java SPI结合使用

时间:2014-11-05 18:56:30

标签: java design-patterns osgi

我有一个项目,我正在使OSGi兼容。该代码依赖于Java SPI来添加实现(META-INF / services)。我不想在OSGi环境中使用SPI(例如使用SPI Fly),我宁愿使用OSGi方式。但是,我希望对非OSGi环境保持SPI支持。我的方法如下:

工厂看起来像这样(使用BND注释):

@Component
class MyFactory implements MyFactoryService {

    public MyFactory() {
         ...
    }

    //This method is reserved for non-OSGi use (uses SPI to find implementations)
    public static MyFactory newInstance() {
        MyFactory ret = new MyFactory();
        Iterator<MyDiscoverable> i = ServiceLoader.load(MyDiscoverable.class).iterator();
        while (i.hasNext()) {
            ret.addFactory(i.next());
        }
    }

    @Reference
    public void addFactory(MyDiscoverable f) {
        ...
    }

}

在OSGi上下文中,MyFactory位于OSGi私有包中,必须使用MyFactoryService接口通过服务注册表检索。然后使用@Reference注释(或者更确切地说是从它生成的声明性服务)由OSGi框架填充MyFactory。

如果MyDiscoverable的实现需要其他类似于MyFactory的工厂,我在MyFactory.newInstance()中添加了类似的东西:

 public static MyFactory newInstance() {
        MyFactory ret = new MyFactory();
        MyOtherFactory other = MyOtherFactory.newInstance();
        Iterator<MyDiscoverable> i = ServiceLoader.load(MyDiscoverable.class).iterator();
        while (i.hasNext()) {
            MyDiscoverable x = i.next();
            //This method is also annotated with @Reference in the implementation, to support OSGi use
            x.setReference(other);
            ret.addFactory(x);
        }
}

这在OSGi和SPI上下文中都可以正常工作。我遇到的一个问题是工厂必须知道每个实现可能需要哪些其他工厂(并提供它们)。这是因为我没有找到一种方法让实现创建自己的工厂实例而不会使它与OSGi不兼容。这种限制在大多数情况下是可控的,但作为一般解决方案是不可接受的。

有更好的方法吗?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:0)

一种方法是明确地向MyDiscoverable提供通过newInstance创建的信息,例如。 x.populateWithSPI()。这样,setReference可以由实现本身调用,例如MyOtherFactory.newInstance()

答案 1 :(得分:0)

OSGI使用IOC容器,并且像所有IOC容器(Guice,Spring等)一样,IOC容器必须负责类的实例化。

SPI支持在运行时发现实现,并且SPI将这些实现实例化为发现的一部分。

如果没有hack,就不能将相同的类用于SPI和OSGI(或任何其他IOC容器),因为它们具有不同的实例化类的方法。我建议将两种方法分开。

您可以围绕MyFactoryService创建一个由SPI使用的包装器类。该类可以称为MyFactoryServiceProvider,它可以返回MyFactoryService的类引用供您实例化,然后将MyDiscoverable的引用传递给它。