在OSGI环境中的依赖注入

38

首先一些背景:

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

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

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

那么,我的问题如下:

  1. 声明式服务与Guice或Spring等“传统”DI相比如何?它们解决了相同的问题还是针对不同的问题?
  • 目前我见过的所有OSGI特定解决方案都缺乏DI的作用域概念。例如,Guice + guice-servlet具有请求范围依赖项,这使得编写Web应用程序非常简洁和易于理解。我在文档中错过了吗?还是这些框架都没有涉及到此类问题?
  • JSR 330和基于OSGI的DI是两个不同的世界吗?例如,iPOJO带有自己的注释,而Felix SCR Annotations则似乎是完全不同的领域。
  • 有人有构建基于OSGI的系统和DI的经验吗?甚至在github上有一些示例代码吗?
  • 是否有人将不同的技术(如Guice和iPOJO)结合使用,还是这只是一个疯狂的想法?

  • 1
    相反地:感谢您提出措辞得当(因此“长”)的问题 :-) - DerMike
    6个回答

    29

    总体方法

    在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注释似乎是完全不同的领域。

    是的,它们是不同的。您应该记住,尽管Spring和Guice更为主流,但OSGi服务更为复杂,支持更多用例。在OSGi捆绑包(以及隐式服务)中,自由地来来去去。这意味着当您有一个依赖于刚刚变得不可用的服务的组件时,您的组件将被停用。或者当您收到一系列组件(例如Servlet实现)并且其中一个被停用时,您会受到通知。据我所知,Spring和Guice都不支持这种方式,因为它们的连线是静态的。

    这是OSGi给您的极大灵活性。

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

    Sling样本SVN存储库中有大量示例。您应该在那里找到大部分所需内容。

    有人使用不同的技术,如Guice和iPOJO在一起吗,还是这只是一个疯狂的想法?

    如果您的框架配置了JSR 330注释,则使用Guice或Spring或适合您的任何其他工具在运行时对其进行配置是有意义的。但是,正如Neil Bartlett指出的那样,这将无


    完全同意Robert的观点,如果已经在使用Sling,则无需添加额外的层。与OSGi服务组件运行时相比,Spring或Guice可能会增加一些附加功能,但通常情况下,移动部件较少通常会导致更容易调试和维护的系统。我建议阅读《OSGi in Action》书(http://manning.com/hall/)以获得有关Sling中使用的标准OSGi服务带来的概述。 - Bertrand Delacretaz
    关于Sling样例,我建议看一下Slingbucks,它在使用SCR服务和注释方面相当简单和“现代化” - 它位于http://svn.apache.org/repos/asf/sling/trunk/samples/slingbucks/。我还为我的ApacheCon“OSGi for mere mortals”演讲创建了一个简单的RESTful服务器示例,您可以在https://github.com/bdelacretaz/OSGi-for-mere-mortals找到它,并在Sling之外使用类似的技术。 - Bertrand Delacretaz
    哇,首先,非常感谢您详细的回答!我会尝试在原始问题上进行更新,评论似乎不是很适合进行许多更新。 - ilikeorangutans
    正如Robert所提到的,作用域方法是特定于领域的。因此,它们应该成为特定于领域的框架(例如Web框架)和API的一部分,这些API通过相关上下文传递。在Sling的情况下,这将是SlingHttpServletRequest(以及Response)接口,它们使您可以访问请求周围的所有内容(即预构建的资源解析器+使用请求用户进行身份验证的会话)。如果需要,请将其传递给需要这些请求特定对象的服务。在我看来,将这种作用域方法与DI混合只会导致太多的“魔法”代码。 - Alexander Klimetschek

    14
    我只是想在Robert的优秀答案上添加一些信息,特别是关于JSR330和DS的内容。
    声明式服务、Blueprint、iPOJO以及其他OSGi“组件模型”,主要用于注入OSGi服务。这些服务比常规依赖项更难处理,因为它们可以随时出现或消失,包括对外部事件(例如网络断开)或用户操作(例如删除捆绑包)做出响应。因此,所有这些组件模型都提供了一个额外的生命周期层,用于纯依赖注入框架。
    这就是为什么DS注释与JSR330注释不同的主要原因... JSR330注释没有提供足够的语义来处理生命周期。例如,它们没有说明:
    - 依赖项应该在何时被注入? - 当依赖项当前不可用时应该怎么办(即,它是可选还是强制性的)? - 当使用的服务消失时应该怎么办? - 我们可以动态地从一个服务实例切换到另一个服务实例吗? - 等等...
    不幸的是,因为组件模型主要专注于服务 - 即,捆绑包之间的连接 - 所以它们相对于在包内布线依赖项而言有些简陋(尽管Blueprint确实为此提供了一些支持)。
    在包内布线依赖项应该不会有问题,可以使用现有的DI框架来实现。例如,我曾经有一个客户使用Guice来连接某些声明式服务组件的内部部件。但是,我倾向于质疑这样做的价值,因为如果您需要在捆绑包内部使用DI,则说明您的捆绑包可能过大且不连贯。
    请注意,非常重要的一点是不要使用传统的DI框架来连接捆绑包之间的组件。如果DI框架需要访问另一个捆绑包中的类,则该捆绑包必须公开其实现细节,这会打破我们在OSGi中寻求的封装性。

    1
    你好Neil,感谢你的回答。我的观察与你类似,OSGI DI框架主要关注于OSGI服务,而不是连接对象图。我之所以问JSR330 DI的原因是我们已经有很多使用它们的代码,所以最简单的方法就是放入Guice并在bundle内部使用一个注入器。然而,问题随之而来,如何通过/作为OSGI服务公开由Guice创建的实例? - ilikeorangutans
    1
    连接OSGi服务就是连接对象图...其中该图穿越模块边界。如果您要使用类似DS的东西,那么您可以在DS组件内完全创建一个Guice“模块”(术语冲突!)。 - Neil Bartlett

    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的管理功能,但你只需要安装相同的bundle即可运行良好。

    2

    我可以推荐使用Bnd,特别是在使用Eclipse IDE时,还需要使用Bndtools。这样可以避免使用XML描述DS,而是使用注释。DI有一个特殊的Reference注释。这个注释也有一个过滤器,你可以引用特定子集的服务。


    Bnd是Robert提到的maven-scr-plugin底层使用的工具。 - Bertrand Delacretaz
    是的,这些我已经在使用了。我使用Apache Felix的BND插件自动生成XML文件。 - ilikeorangutans

    1

    这里遇到了一个类似的架构问题 - 就像Robert在他的回答中提到的:

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

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

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

    这里的缺点是您需要在接口设计中添加ResourceResolverSessions参数。

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

    有没有一种可靠的方法可以获取每个请求的ResourceResolver,而无需将其传递到每个服务方法中?

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

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


    1

    我目前的项目正在使用OSGi和DI,我选择了Gemini Blueprint,因为它是SPRING DYNAMIC MODULES的第二个版本。基于这些信息,我建议您阅读Spring Dynamic Modules in Action。这本书将帮助您理解如何构建架构以及为什么这样做是有益的 :)


    嗨Sergii,感谢你的建议。我一定会去看看Spring动态模块。不确定我怎么错过了它!;) - ilikeorangutans
    从第二个版本开始阅读,称为Eclipse Gemini Blueprint http://www.eclipse.org/gemini/blueprint/ - Sergii Zagriichuk
    如果您想使用Spring DM,则请使用新的Gemini。旧的Spring DM在OSGi中存在一些严重的类加载问题,因为它与原始的Spring保持了太多的兼容性。另一个选择是Aries Blueprint,它比Gemini更轻量级,但功能较少。请参见http://gnodet.blogspot.de/2010/03/spring-dm-aries-blueprint-and-custom.html。 - Christian Schneider

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接