26

我正在处理使用 Spring 进行 DI/bean 管理的非常大的 JSF/Facelets 应用程序。我的应用程序具有模块化结构,我目前正在寻找标准化模块化的方法。

我的目标是从多个模块(可能相互依赖)组成一个 Web 应用程序。每个模块可能包含以下内容:

  • 课程;
  • 静态资源(图片、CSS、脚本);
  • 小面模板;
  • 托管 bean - Spring 应用程序上下文,具有请求、会话和应用程序范围的 bean(替代方案是 JSF 托管 bean);
  • Servlet API 的东西 - servlet、过滤器、侦听器(这是可选的)。

我想避免(几乎不惜一切代价)需要将模块资源(如 Facelets 模板)复制或提取到 WAR 或扩展web.xmlfor 模块的 servlet、过滤器等。添加模块必须足够(JAR, bundle, artifact, ...) 到 Web 应用程序 ( WEB-INF/lib, bundles, plugins, ...) 以使用此模块扩展 Web 应用程序。

目前,我使用自定义模块化解决方案来解决此任务,该解决方案很大程度上基于使用类路径资源:

  • 特殊资源 servlet 提供来自类路径资源 (JAR) 的静态资源。
  • 特殊的 Facelets 资源解析器允许从类路径资源加载 Facelet 模板。
  • Spring 通过模式 classpath*:com/acme/foo/module/applicationContext.xml加载应用程序上下文 - 这会加载模块 JAR 中定义的应用程序上下文。
  • 最后,一对委托 servlet 和过滤器将请求处理委托给模块中在 Spring 应用程序上下文中配置的 servlet 和过滤器。

前几天我读了很多关于 OSGi 的文章,我正在考虑如何(以及是否)可以将 OSGi 用作标准化的模块化方法。我在考虑如何使用 OSGi 解决单个任务:

  • 静态资源 - 想要导出静态资源的 OSGi 包ResourceLoader在包上下文中注册一个实例。中央ResourceServlet使用这些资源加载器从包中加载资源。
  • Facelet 模板 - 与上面类似,中心ResourceResolver使用捆绑注册的服务。
  • 托管 bean -我不知道如何使用#{myBean.property}ifmyBean在其中一个包中定义的表达式。
  • Servlet API 的东西 - 使用 WebExtender/Pax Web 之类的东西来注册 servlet、过滤器等。

我的问题是:

  • 我在这里发明自行车吗?有没有标准的解决方案?我发现提到了 Spring Slices,但找不到太多关于它的文档。
  • 您认为 OSGi 是完成所描述任务的正确技术吗?
  • 我的 OSGI 应用程序草图或多或少正确吗?
  • 应如何处理托管 bean(尤其是请求/会话范围)?

我一般会感谢您的评论。

4

5 回答 5

13

你的目标听起来是可行的,但有几点需要注意:

视图层:首先,您的视图层听起来有点臃肿。还有其他方法可以通过使用自定义组件来模块化 JSF 组件,这将避免尝试创建像后期绑定托管 bean 这样引人注目的东西所涉及的麻烦。

模块本身:其次,您的模块似乎不是特别模块化。您的第一个项目符号列表听起来好像您正在尝试创建可互操作的 Web 应用程序,而不是模块本身。我对模块的想法是,每个组件都有一个明确定义的、或多或少离散的目的。就像ex是vi的基础。如果你走的是 OSGi 路线,那么我们应该像这样定义模块化:为了讨论方便,模块化意味着组件是可热交换的——也就是说,可以在不破坏应用程序的情况下添加和删除它们。

依赖关系:我有点担心您将模块描述为“可能相互依赖”。您可能(我希望)已经知道这一点,但是您的依赖项应该形成一个有向无环图。一旦你引入了循环依赖,就应用程序的最终可维护性而言,你要求一个伤害的世界。OSGi 的最大弱点之一是它不能防止循环依赖,因此由您来强制执行。否则,您的依赖项将像葛一样增长,并逐渐阻塞系统生态系统的其余部分。

Servlet: Fuhgeddaboudit。您不能将 servlet 后期绑定到 Web 应用程序中,直到 Servlet 3.0 规范投入生产(正如 Pascal 指出的那样)。要启动单独的实用程序 servlet,您需要将其放入自己的应用程序中。


好的,警告就这么多。让我们想想这可能是如何工作的:

您已经定义了自己的 JSF 模块来执行... 究竟是什么?让我们给它一个明确的、相当琐碎的目的:登录屏幕。因此,您创建了登录屏幕,使用 OSGi 将其后期绑定到您的应用程序中,然后……然后呢?如果您尚未在 .jspx 页面中定义登录功能,应用程序如何知道它存在?应用程序如何知道导航到它不知道的东西?

有一些方法可以使用条件包含等(例如,<c:if #{loginBean.notEmpty}>)来解决这个问题,但是,就像你说的,当你的托管 loginBean 存在于另一个可能还没有被引入应用程序的模块中时,事情会变得有点棘手。事实上,除非 loginBean 存在,否则您将得到一个 servlet 异常。所以你会怎么做?

您在其中一个模块中定义 API。您打算在模块之间共享的所有托管 bean 必须在此 API 层中指定为接口。并且您的所有模块都必须具有它们打算使用的任何这些接口的默认实现。并且这个 API 必须在所有可互操作的模块之间共享。然后,您可以使用 OSGi 和 Spring 将指定的 bean 与其实现连接在一起。

我需要花点时间指出这不是我处理这个问题的方式。一点也不。考虑到像登录页面这样简单,甚至像股票图表那样复杂,我个人更喜欢创建一个自定义 JSF 组件。但是,如果要求是“我希望我的托管 bean 是模块化的(即,可热插拔等)”,这是我所知道的使其工作的唯一方法。而且我什至不完全确定它起作用。这个电子邮件交流表明这是一个 JSF 开发人员才刚刚开始研究的问题。

我通常认为托管 bean 是视图层的一部分,因此我仅将它们用于视图逻辑,并将其他一切委托给服务层。在我看来,使托管 bean 后期绑定是将它们从视图层提升到业务逻辑中。所有这些教程都如此专注于服务是有原因的:因为大多数时候您想考虑您的应用程序“无头”运行需要什么,以及如果为例如,您希望它在 Android 手机上运行,​​并具有所有功能。

但听起来你正在使用的很多东西本身就是视图逻辑——例如,需要交换不同的视图模板。OSGi/Spring 应该能够提供帮助,但是您需要在您的应用程序中选择可用的实现:这几乎就是 OSGi 的服务注册表的构建目的。

这留下了静态资源。您可以将它们模块化,但请记住,您需要定义一个接口来检索这些资源,并且您需要提供一个默认实现,以便您的应用在它们不存在时不会阻塞。如果 i18n 是一个考虑因素,这可能是一个不错的选择。如果您想真正冒险,那么您可以将您的静态资源推入 JNDI。这将使它们完全可热交换,并避免您尝试以编程方式解决使用哪个实现的痛苦,但也有一些缺点:任何失败的查找都会导致您的应用程序抛出 NamingException。而且太过分了。JNDI 通常在 Web 应用中用于应用配置。

至于你剩下的问题:

我在这里发明自行车吗?有没有标准的解决方案?

你是,一点点。我见过做这种事情的应用程序但你似乎偶然发现了一组相当独特的要求。

您认为 OSGi 是完成所描述任务的正确技术吗?

如果您需要模块可热插拔,那么您的选择是 OSGi 和更轻量级的 ServiceLocator 接口。

我的 OSGI 应用程序草图或多或少正确吗?

如果不知道更多关于您的组件边界在哪里,我真的无法判断。目前,听起来您可能正在推动 OSGi 做超出其能力范围的事情。

但不要相信我的话。我在这些地方 找到了 其他阅读材料。

既然您询问 Spring Slices,这应该足以让您入门。您将需要一个 Git 客户端,看起来您将通过查看源代码来训练自己使用该应用程序。这是非常早期的原型代码。

于 2010-04-13T16:18:10.517 回答
3

我在当前的项目中面临同样的问题。在我看来,就标准和未来支持而言,OSGi 是最好和最干净的解决方案,但目前如果您尝试在 Web 应用程序中使用它,您可能会遇到一些问题:

  1. Web Container 和 OSGi 平台之间还没有很好的集成解决方案。
  2. OSGi 对于只搜索简单的模块化架构的自定义构建 Web 应用程序来说可能太多了。如果我的项目需要支持不是 100% 受我们控制的第三方扩展,如果项目需要热重新部署、插件之间的严格访问规则等,我会考虑 OSGi。

基于类加载器和资源过滤器的自定义解决方案似乎非常适合我。作为示例,您可以研究Hudson 源代码或 Java 插件框架 (JPF) 项目 (http://jpf.sourceforge.net/)。

至于扩展 web.xml,我们可能很幸运 Servlet 3.0 规范(http://today.java.net/pub/a/today/2008/10/14/introduction-to-servlet-3.html#可插拔性和可扩展性)。

于 2010-11-22T10:35:42.243 回答
2

Servlet 3.0 规范引入的“web 模块部署描述符片段”(又名 web-fragment.xml)在这里会很好。规范将其定义为:

Web 片段是 Web 应用程序的逻辑分区,这样 Web 应用程序中使用的框架可以定义所有工件,而无需要求开发人员在 web.xml 中编辑或添加信息。

不过,Java EE 6 现在可能不是您的选择。不过,这将是标准化的解决方案。

于 2010-04-09T21:53:22.680 回答
1

Enterprise OSGi 是一个相当新的领域,所以不要认为您会得到直接满足您需求的解决方案。也就是说,我发现 Equinox 中缺少的一件事(eclipse 背后的 osgi 引擎,因此拥有最大的用户群!)是一致的配置 / DI 服务。在我最近的项目中,我们有一些类似的需求,并最终构建了一个简单的配置 osgi 服务。

模块化应用程序固有的问题之一是 DI,因为模块可见性在某些情况下可能会阻止类访问。我们使用注册伙伴策略解决了这个问题,这不是太理想但很有效。

除了配置之外,您还可以查看最近发布的 Equinox 书籍,以获取有关使用 OSGi 作为创建模块化应用程序的基础的指南。这些示例可能特定于 Equinox,但这些原则适用于任何 OSGi 框架。链接 - http://equinoxosgi.org/

于 2010-04-09T21:22:45.683 回答
0

您应该查看 Spring DM Server(它正在转换为 Eclipse Virgo,但尚未发布)。最近刚刚发布的 OSGi 企业规范中有很多好东西。

我想,一些 Spring DM 教程会有所帮助。但是,是的,可以使用标准模块化从 Web 包外部加载资源和类。在这方面,这是一个很好的选择。

至于会话上下文 - 它会按照您在会话中的期望进行处理。但是,您可能会遇到在 Web 包之间共享该会话的问题,甚至不确定是否可行。

您还可以查看单个 Web 包,然后使用例如 Eclipse 扩展注册表来扩展您的 Web 应用程序的功能。

于 2010-04-04T20:04:19.057 回答