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

lex*_*ore 26 java spring osgi modularization

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

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

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

我想避免(几乎不惜一切代价)是需要将模块资源(如Facelets模板)复制或提取到WAR或扩展web.xmlfor模块的servlet,过滤器等.它必须足以添加模块(JAR,捆绑,伪影,...)到web应用(WEB-INF/lib,bundles,plugins,...)来延伸,以与该模块的Web应用程序.

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

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

最后几天我读了很多关于OSGi的内容,我正在考虑,如何(以及如果)我可以将OSGi用作标准化的模块化方法.我在考虑如何使用OSGi解决单个任务:

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

我的问题是:

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

我一般都很感谢你的评论.

rtp*_*son 13

你想要做的事情听起来可行,但有几点需要注意:

视图层:首先,您的视图层听起来有点过度填充.还有其他方法可以通过使用自定义组件来模块化JSF组件,这样可以避免尝试创建与后期绑定托管bean一样引人注目的事情.

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

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

Servlets: 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的Service Registry所要做的.

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

至于你剩下的问题:

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

你是,有点.我已经看到了这样做的应用程序样的事情,但你似乎已经跌进一个相当独特的要求.

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

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

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

如果不了解组件边界的位置,我真的无法分辨.目前,听起来你可能正在推动OSGi做的比它能做的更多.

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

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