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