如何使用OSGi模块化JSF/Facelets/Spring应用程序?

2022-09-01 17:47:02

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

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

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

我想避免的(几乎不惜一切代价)是需要将模块资源(如Facelets模板)复制或提取到WAR或扩展for模块的servlet,过滤器等。将模块(JAR、捆绑包、工件等)添加到 Web 应用程序 (, , , ...) 以使用此模块扩展 Web 应用程序必须足够了。web.xmlWEB-INF/libbundlesplugins

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

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

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

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

我的问题是:

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

我将不胜感激您的评论。


答案 1

你打算做的事情听起来是可行的,但有一些注意事项:

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

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

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

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


好吧,这么多的警告。让我们考虑一下这可能是如何工作的:

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

有一些方法可以使用条件包含等(例如,)来解决此问题,但是,就像你说的,当你的托管loginBean存在于另一个可能尚未引入应用程序的模块中时,事情会变得有点毛茸茸的。事实上,你会得到一个 servlet 异常,除非该 loginBean 存在。那你该怎么办?<c:if #{loginBean.notEmpty}>

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

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

我通常认为托管 Bean 是视图层的一部分,因此我只将它们用于视图逻辑,并将其他所有内容委托给服务层。在我看来,使托管 Bean 延迟绑定是将它们从视图层提升到业务逻辑中。所有这些教程都如此专注于服务是有原因的:因为大多数时候,你想要考虑你的应用程序“无外设”运行需要什么,以及“皮肤”你的视图是多么容易,例如,如果你想让它在Android手机上运行,以及它的所有功能。

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

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

至于你剩下的问题:

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

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

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

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

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

如果不更多地了解组件边界的位置,我真的无法分辨出来。目前,听起来你可能正在推动OSGi做更多的事情,而不是它能够做的事情。

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

既然你问了Spring Slices,这应该足以让你开始。你需要一个 Git 客户端,看起来你将通过查看源代码来训练自己。这是非常早期的原型代码。


答案 2

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

  1. Web容器和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#pluggability-and-extensability)。


推荐