38

首先是一些背景:

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

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

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

所以,我的问题如下:

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

抱歉,这个问题很长。

非常感谢任何反馈。


更新

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

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

感谢您的所有回答,您对我很有帮助!

4

6 回答 6

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 也是如此。

如果您发现自己需要按用户工作,最简单的方法是让服务接收 JCRSession或 SlingResourceResolver并使用它们来执行您需要的工作。结果将根据当前用户的权限自动调整,无需任何额外的努力。

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

是的,它们是不同的。您应该记住,虽然 Spring 和 Guice 更主流,但 OSGi 服务更复杂,支持更多用例。在 OSGi 中,捆绑包(以及隐含的服务)可以随时来来去去。

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

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

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

Sling Samples SVN 存储库中有大量示例。你应该在那里找到大部分你需要的东西。

是否有人同时使用 Guice 和 iPOJO 等不同的技术,或者这只是一个疯狂的想法?

如果你有配置了 JSR 330 注释的框架,那么在运行时使用 Guice 或 Spring 或任何适合你的东西来配置它们是有意义的。然而,正如 Neil Bartlett 所指出的,这不会跨捆绑工作。

于 2012-06-23T12:21:03.687 回答
14

我只想为罗伯特的出色答案添加更多信息,特别是关于 JSR330 和 DS。

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

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

  • 什么时候应该注入依赖?
  • 当依赖项当前不可用时我们应该怎么做(即它是可选的还是强制的)?
  • 当我们正在使用的服务消失时,我们应该怎么做?
  • 我们可以动态地从一个服务实例切换到另一个实例吗?
  • ETC...

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

使用现有的 DI 框架来连接包内的依赖项应该没有问题。例如,我有一个客户使用 Guice 来连接一些声明式服务组件的内部部件。然而我倾向于质疑这样做的价值,因为如果你在你的包中需要 DI,这表明你的包可能太大而且不连贯。

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

于 2012-06-24T16:49:21.033 回答
3

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

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

我的网站上还有一些 Blueprint 和 Apache Karaf 的教程: http ://www.liquid-reality.de/display/liquid/Karaf+Tutorials

在您使用嵌入式 felix 的环境中,它会有点不同,因为您没有 Karaf 的管理功能,但您只需要安装相同的包,它应该可以很好地工作。

于 2012-06-23T16:03:21.927 回答
2

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

于 2012-06-23T08:53:59.107 回答
1

我在当前项目中使用 osgi 和 DI,我选择了双子座蓝图,因为它是SPRING DYNAMIC MODULES的第二个版本,基于此信息,我建议您阅读Spring Dynamic Modules in Action。这本书将帮助你理解一些部分和要点如何构建架构以及它为什么好:)

于 2012-06-22T21:44:24.977 回答
1

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

如果您发现自己需要每个用户的工作,最简单的方法是让服务接收 JCR Session 或 Sling ResourceResolver 并使用它们来执行您需要的工作。结果将根据当前用户的权限自动调整,无需任何额外的努力。

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

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

这里的缺点是你需要在你的界面设计中乱扔参数ResourceResolverSessions参数。

最初我们尝试注入ResourceResolverFactory到 DAO 层,这样我们就可以很容易地通过工厂随意访问会话。但是,我们在层次结构中的多个点与会话交互,并且每个请求多次。这导致了会话关闭异常。

有没有办法ResourceResolver可靠地处理每个请求,而不必将其传递给每个服务方法?

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

这似乎是一个常见问题,您希望将关注点分离到服务/dao 代码中,将 JCR 交互留在 DAO 中,类似于 Hibernate,您如何轻松获得Session执行 repo 操作的每个请求?

于 2012-07-25T21:59:29.737 回答