OSGI环境中的依赖注入

时间:2012-06-22 21:33:04

标签: java dependency-injection osgi guice apache-felix

首先是一些背景:

我正在开发一些基于Apache Sling的webapp原型代码,它基于OSGI并在Apache Felix上运行。尽管我认为我现在已经掌握了大部分概念,但我对OSGI仍然相对较新。然而,令我困惑的是,我无法找到“完整”依赖注入(DI)框架。我使用声明服务(DS)成功地使用了基本的DI。但我的理解是DS用于引用 - 我该怎么说呢? - OSGI将注册的服务和组件放在一起。为此它工作正常,但我个人使用像Guice这样的DI框架将整个对象图连接在一起,并将对象放在正确的范围内(例如,考虑@RequestScoped@SessionScoped)。但是,我所看到的OSGI特定框架似乎都没有支持这个概念。

我开始阅读有关OSGI blueprintsiPOJO的内容,但这些框架似乎更关注将OSGI服务连接在一起而不是提供完整的DI解决方案。我不得不承认我还没有做任何样品,所以我的印象可能不正确。

作为Guice的扩展,我已经尝试了Peaberry,但是我发现文档很难找到,虽然我得到了基本的DI工作,但是很多guice-servlet的高级功能(自动注入过滤器) ,servlets等)根本不起作用。

所以,我的问题如下:

  1. 声明式服务与Guice或Spring等“传统”DI相比如何?他们是解决同样的问题,还是针对不同的问题?
  2. 到目前为止,我见过的所有OSGI特定解决方案都缺乏DI的范围概念。例如,Guice + guice-servlet具有请求范围依赖性,这使得编写Web应用程序非常简洁。我是否只是错过了文档中的内容,或者这些框架没有涵盖这些问题?
  3. 基于JSR 330和OSGI的DI有两个不同的世界吗?例如,iPOJO带来的own annotationsFelix SCR Annotations似乎是一个完全不同的世界。
  4. 有没有人有建立基于OSGI的系统和DI的经验?甚至可能在github上有一些示例代码?
  5. 有没有人像Guice和iPOJO一样使用不同的技术,还是只是一个疯狂的想法?
  6. 很抱歉这个问题很长。

    非常感谢任何反馈。


    更新

    Scoped injection :范围注入是一种有用的机制,可以自动注入特定生命周期中的对象。例如,您的一些代码依赖于作为servlet过滤器的一部分创建的Hibernate会话对象。通过标记依赖关系,容器将自动重建对象图。也许只有不同的方法呢?

    JSR 330 vs DS :从你所有优秀的答案中我看到这些是两个不同的东西。这提出了一个问题,如何处理在OSGI上下文中使用时使用JSR 330注释的第三方库和框架?什么是好方法?在Bundle中运行JSR 330容器?

    我感谢您的所有答案,您一直非常乐于助人!

6 个答案:

答案 0 :(得分:28)

总体方法

使用Apache Sling进行依赖注入的最简单方法是使用maven-scr-plugin

使用整个代码库。

您可以注释您的java类,然后在构建时调用SCR插件,可以是Maven插件,也可以是Ant任务。

例如,要注册servlet,您可以执行以下操作:

@Component // signal that it's OSGI-managed
@Service(Servlet.class) // register as a Servlet service
public class SampleServlet implements Servlet {   
   @Reference SlingRepository repository; // get a reference to the repository    
}

具体答案

  

声明式服务与Guice或Spring等“传统”DI相比如何?他们是解决同样的问题,还是针对不同的问题?

他们解决了同样的问题 - 依赖注入。但是(见下文)它们的构建也考虑了服务可以随时出现或消失的动态系统。

  

到目前为止,我见过的所有OSGI特定解决方案都缺乏DI的范围概念。例如,Guice + guice-servlet具有请求范围依赖性,这使得编写Web应用程序非常简洁。我是否只是错过了文档中的内容,或者这些框架没有涵盖这些问题?

我没有在SCR世界中看到任何方法来添加会话范围或请求范围的服务。但是,SCR是一种通用方法,可以在更具体的层处理范围。

由于您正在使用Sling,我认为几乎不需要会话范围或请求范围的绑定,因为Sling为每个请求都有内置对象,这些对象是为当前用户正确创建的。

一个很好的例子是JCR会话。它是使用正确的权限自动构建的,实际上是一个请求范围的DAO。 Sling resourceResolver也是如此。

如果您发现自己需要按用户工作,最简单的方法是获得接收JCR Session或Sling ResourceResolver的服务,并使用这些服务执行您需要的工作。结果将自动调整为当前用户的权限,而无需任何额外的努力。

  

JSR 330和OSGI基于DI两个不同的世界吗?例如,iPOJO带来了自己的注释,而Felix SCR Annotations似乎是一个完全不同的世界。

是的,他们是不同的。您应该记住,尽管Spring和Guice更为主流,但OSGi服务更复杂并且支持更多用例。在OSGi中,捆绑包(以及隐式服务)随时可以自由出入。

这意味着当您拥有一个依赖于刚刚变为不可用的服务的组件时,您的组件将被停用。或者当您收到组件列表(例如,Servlet实现)并且其中一个已停用时,您会收到通知。据我所知,Spring和Guice都不支持这一点,因为他们的布线是静态的。

这是OSGi为您提供的极大灵活性。

  

有没有人有建立基于OSGI的系统和DI的经验?甚至可能是github上的一些示例代码?

Sling Samples SVN repository中有大量样本。你应该找到你需要的大部分内容。

  

有人使用Guice和iPOJO等不同的技术,还是只是一个疯狂的想法?

如果你有使用JSR 330注释配置的框架,那么在运行时使用Guice或Spring或任何适合你的方法配置它们是有意义的。然而,正如尼尔巴特利特所指出的那样,这不会起到跨包的作用。

答案 1 :(得分:14)

我想向罗伯特的优秀答案添加更多信息,特别是关于JSR330和DS。

声明服务,蓝图,iPOJO和其他OSGi“组件模型”主要旨在注入OSGi服务。这些比常规依赖更难处理,因为它们可以随时进出,包括响应外部事件(例如网络断开连接)或用户操作(例如删除捆绑包)。因此,与纯依赖注入框架相比,所有这些组件模型都提供了额外的生命周期层。

这是DS注释与JSR330不同的主要原因...... JSR330没有提供足够的语义来处理生命周期。例如,他们没有说什么:

  • 何时应该注入依赖项?
  • 当依赖关系当前不可用时,我们应该怎么做(即,它是可选的还是必需的)?
  • 当我们使用的服务消失时我们该怎么办?
  • 我们可以动态地从一个服务实例切换到另一个服务吗?
  • 等...

不幸的是,因为组件模型主要关注服务 - 即 bundle之间的链接 - 它们在连接依赖关系内部方面相对简陋捆绑(尽管蓝图确实为此提供了一些支持)。

使用现有的DI框架来连接bundle中的依赖关系应该没有问题。例如,我有一个客户使用Guice来连接一些Declarative Services组件的内部部分。但是我倾向于质疑这样做的价值,因为如果你在你的包中需要DI,那么它表明你的包可能太大而且不连贯。

请注意,不要使用传统的DI框架连接 捆绑包之间的组件非常重要。如果DI框架需要从另一个bundle访问一个类,那么另一个bundle必须公开它的实现细节,这打破了我们在OSGi中寻找的封装。

答案 2 :(得分:3)

我在使用Aries Blueprint构建应用程序方面有一些经验。它有一些关于OSGi服务和配置管理支持的非常好的功能。

如果您搜索一些很好的示例,请查看Apache Karaf的代码,该代码使用蓝图进行所有布线。 见http://svn.apache.org/repos/asf/karaf/

我的网站上也有蓝图和Apache Karaf的一些重要内容: http://www.liquid-reality.de/display/liquid/Karaf+Tutorials

在使用嵌入式felix的环境中,由于您没有Karaf的管理功能,因此您需要安装相同的软件包,它应该可以很好地工作。

答案 3 :(得分:2)

我可以推荐Bnd,如果你也使用Eclipse IDE,那么也可以使用Bndtools。有了它,您可以避免在XML中描述DS并使用注释。 DI有一个特殊的Reference注释。这个还有一个过滤器,您只能引用一个特殊的服务子集。

答案 4 :(得分:1)

我正在使用osgi和DI作为当前的项目,我选择了双子座蓝图,因为它是SPRING DYNAMIC MODULES的第二个版本,根据这些信息,我建议你阅读Spring Dynamic Modules in Action。本书将帮助您理解一些部分并指出如何构建体系结构以及为什么它很好:)

答案 5 :(得分:1)

在这里遇到类似的架构问题 - 正如罗伯特在他的回答中提到的那样:

  

如果您发现自己需要按用户工作,最简单的方法是   拥有接收JCR会话或Sling ResourceResolver的服务   并使用它们来完成你需要的工作。结果将是   无需自动调整当前用户的权限   任何额外的努力。

从此推断(以及我目前编写的代码),一种方法是将@param resourceResolver添加到任何@Service方法,以便您可以传递适当的请求范围对象以供使用执行链。

具体来说,我们有一个XXXXService / XXXXDao层,从XXXXServlet / XXXXViewHelper / JSP等价物调用。因此,通过OSGI @Service注释管理所有这些组件,我们可以轻松连接整个堆栈。

这方面的缺点是你需要使用ResourceResolverSessions参数来破坏你的界面设计。

最初我们尝试将ResourceResolverFactory注入DAO层,以便我们可以通过工厂轻松访问会话。但是,我们在层次结构中的多个点处与会话进行交互,并且每个请求多次交互。这导致了会话关闭的异常。

有没有办法可靠地获取每个请求ResourceResolver而无需将其传递到每个服务方法?

使用服务层上的请求范围注入,您可以将ResourceResolver作为构造函数arg&请改用实例变量。当然,这里的缺点是您必须考虑请求范围与原型范围服务代码并相应地分开。

这似乎是一个常见的问题,你想把关注点分离到service / dao代码中,将JCR交互留在DAO中,类似于Hibernate,你如何轻松获取每个请求{ {1}}执行回购操作?