如何创build一个模块化的JSF 2.0应用程序?

我有一个定义良好的界面的应用程序。 它使用CDI来parsing模块,(具体来说,它使用API​​接口上的实例<>注入点来parsing模块),并通过接口传回各种数据。 我有意将API和实现保持分开,并且模块只从APIinheritance以避免紧耦合,应用程序只通过运行时依赖知道模块,并通过API完成数据传递。 应用程序在没有模块的情况下运行正常,只需将jar放入WEB-INF / lib文件夹并重新启动应用程序服务器即可添加。

我遇到的问题是我希望模块创build视图的一部分,因此我希望以可移植的方式调用JSF组件,或者从模块中进行包含以获得它呈现其视图。 我已经解决了我想要调用的模块,并准备好引用模块的接口。 我最初的想法是做一个ui:包含要求模块提供视图模板的地方,但我不知道如何以有意义的方式回答该查询,因为视图分辨率是从应用程序完成的根,而不是图书馆的根。

执行摘要是我不知道如何使用JSF for .xhtml(模板/组件)文件跳过从应用程序到库的差距。

使用CC会很好,但是如何在运行时指定我需要特定的CC实例,而不是将其硬编码到页面中?

我当然可以直接调用应用程序代码,并要求它提供标记,但是这看起来非常暴躁,一旦我有了标记,我不确定如何告诉JSF来评估它。 也就是说,我可以想象一个组件,将采取资源path,抢标记和评估,返回完成的标记,我只是不知道如何实现。

如果可能的话,我宁愿避免强迫模块开发人员去使用繁重的UIComponent方法,这意味着要么采取一种dynamic的方式来执行ui:include(或一些等效)或者dynamic调用CC的方式。 (我不介意在应用程序中编写UIComponent方法ONCE,如果这使得模块开发人员的生活更容易)

有什么build议,我应该看看这个了? (如果我先find答案,我会在这里发表答案)

我明白,你的问题基本归结为如何包括JAR中的Facelets视图?

你可以通过在JAR中放置一个自定义的ResourceResolver来做到这一点。

 public class FaceletsResourceResolver extends ResourceResolver { private ResourceResolver parent; private String basePath; public FaceletsResourceResolver(ResourceResolver parent) { this.parent = parent; this.basePath = "/META-INF/resources"; // TODO: Make configureable? } @Override public URL resolveUrl(String path) { URL url = parent.resolveUrl(path); // Resolves from WAR. if (url == null) { url = getClass().getResource(basePath + path); // Resolves from JAR. } return url; } } 

在webapp的web.xmlconfiguration如下:

 <context-param> <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name> <param-value>com.example.FaceletsResourceResolver</param-value> </context-param> 

想象一下你在random.jar有一个/META-INF/resources/foo/bar.xhtml ,那么你可以用通常的方式来包含它

 <ui:include src="/foo/bar.xhtml" /> 

甚至是dynamic的

 <ui:include src="#{bean.path}" /> 

注意:由于Servlet 3.0和更新的JBoss / JSF 2.0版本,如果将文件保存在/META-INF/resources文件夹中,则整个ResourceResolver方法不是必需的。 上面的ResourceResolver只在Servlet 2.5或更旧版本的JBoss / JSF版本中是强制的,因为它们在META-INF资源parsing中存在缺陷。

也可以看看:

  • 在JAR中打包Facelets文件(模板,包含,合成)
  • JSF facelets模板包装

我正在寻找关于同一主题的信息,并且遇到了这个链接: 操作方法:使用CDI和PrettyFaces的模块化Java EE应用程序对我来说非常合适。

顺便说一句,你可以避免实现自己的资源parsing器,当你使用缝焊(目前正在集成到Apache deltaspike)这是一个非常有用的库补充CDI(你典型的Java EE 6组件模型)

我也在jsf应用程序中尝试过模块化。 基本上我build立了一个工具栏的模板界面,充满了每个模块提供的button。 通常情况下,您将通过提供一个string列表作为命名对象来做到这一点:

 @Produces @SomethingScoped @Named("topMenuItems") public List<String> getTopMenuItems(){ return Arrays.asList("/button1.xhtml", "/button2.xhtml", "/button3.xhtml"); } 

注意每个button可能来自jsf应用程序的不同模块。 模板接口包含一个面板在哪里

您可以按照以下方式在您的标记中使用它(风险自担);):

 .... xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:ui="http://java.sun.com/jsf/facelets" .... <xy:toolbar> <xy:toolbarGroup> <c:forEach items="#{topMenuItems}" var="link"> <ui:include src="#{link}" /> </c:forEach> </xy:toolbarGroup> </xy:toolbar> <xy:panel> <ui:include src="#{contentPath}"/> </xy:panel> 

这是工具栏和内容面板。

一个简单的button或视图定义可能如下所示:

 <ui:composition ...> <xy:commandButton actionListener="#{topMenuController.switchContent()}" value="Test" id="testbutton" /> </ui:composition> 

让我们命名这个工件view1.xhtml

当按下这个button时(使用actionListener不会触发回发,我们想用ajax重新加载内容)控制器中的switchContentMethod可能会改变getContentPath返回的string:

 public void switchContent(){ contentPath = "/view1.xhtml"; } @Produces @SomethingScoped @Named("contentPath") public String getContentPath(){ return contentPath; } 

现在,您可以使用菜单栏中的button来更改面板中显示的视图,这种方式可以让您无需重新加载页面。

一些build议(或“我学到的东西”):

  1. 您可能想为getTopMenuItems方法select一个大范围
  2. 不要嵌套ui:include标签。 不幸的是,这是不可能的(例如,您的view1.xhtml不能包含其他作品)。 我真的想这样的事情是可能的,因为你可以用这个构build真正的模块化jsf视图,就像没有portlet的portlet一样.. = D
  3. 做ui:包含在像tabview这样的容器组件中也certificate是有问题的。
  4. 通常不build议混合使用JSTL(c:forEach)和JSF。 不过,我发现这是唯一的方式作为用户界面:重复获取评估太迟,例如您包含的内容不会出现。