java 如何创建模块化 JSF 2.0 应用程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6199458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 14:47:18  来源:igfitidea点击:

How to create a modular JSF 2.0 application?

javajsf-2cdijboss-weldmodular

提问by SplinterReality

I have an application with a well defined interface. It uses CDI for resolution of the modules, (Specifically it uses Instance<> injection points on API interfaces to resolve modules) and passes various data back and fourth via the interfaces without issue. I've intentionally kept the API and implementation separate, and the modules only inherit from the API to avoid tight coupling, and the application only knows of the modules through runtime dependancies, and data passing accomplished via the APIs. The application runs fine without the modules, which can be added simply by dropping the jar into the WEB-INF/lib folder and restarting the app server.

我有一个具有明确定义界面的应用程序。它使用 CDI 来解析模块,(特别是它使用 API 接口上的 Instance<> 注入点来解析模块)并通过接口来回传递各种数据,而不会出现问题。我有意将 API 和实现分开,并且模块仅从 API 继承以避免紧密耦合,并且应用程序仅通过运行时依赖关系了解模块,并通过 API 完成数据传递。应用程序在没有模块的情况下运行良好,只需将 jar 放入 WEB-INF/lib 文件夹并重新启动应用程序服务器即可添加模块。

Where I'm running into issues is that I want the modules to create a portion of the view, and I therefor want to invoke, in a portable way, either a JSF component, or do an include from the module in order to have it render its view. I already have resolved what module I want to invoke, and have references to the module's interface ready. The way I initially thought to do this was to do a ui:include that asks the module to supply where it's view template is, but I have no idea how to answer that query in a meaningful way, as view resolution is done from the application root, not the library root.

我遇到的问题是我希望模块创建视图的一部分,因此我想以可移植的方式调用 JSF 组件,或者从模块中执行包含以便拥有它呈现其视图。我已经解决了我想要调用的模块,并且已经准备好对模块接口的引用。我最初想这样做的方法是做一个 ui:include 要求模块提供它的视图模板在哪里,但我不知道如何以有意义的方式回答该查询,因为视图解析是从应用程序完成的根,而不是库根。

The executive summary is that I have no idea how to jump the gap from Application to Library using JSF for .xhtml (template/component) files.

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

Using a CC would be nice, but how do I specify that I want a particular CC instance at runtime, instead of having that hard coded into the page?

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

I can of course invoke the application code directly and ask it for markup, but this seems really brute force, and once I have the markup, I'm not sure exactly how to tell JSF to evaluate it. That said, I can imagine a component that would take the resource path, grab the markup and evaluate it, returning the completed markup, I just don't know how to implement that.

我当然可以直接调用应用程序代码并要求它进行标记,但这似乎非常暴力,一旦我有了标记,我不确定如何告诉 JSF 对其进行评估。也就是说,我可以想象一个组件会采用资源路径,获取标记并对其进行评估,返回完成的标记,我只是不知道如何实现。

I'd rather avoid forcing module developers to go the heavy duty UIComponent approach if possible, which means either a dynamic way of doing ui:include (or some equivalent) or a dynamic way of invoking CCs. (I don't mind coding the UIComponent approach ONCE in the application if that's what it takes to make module developers' lives easier)

如果可能的话,我宁愿避免强迫模块开发人员采用繁重的 UIComponent 方法,这意味着要么以动态方式执行 ui:include(或某种等效方式),要么以动态方式调用 CC。(我不介意在应用程序中一次性编写 UIComponent 方法,如果这能让模块开发人员的生活更轻松的话)

Any suggestions on where I should look to figure this out? (I'll post the answer here if I find it first)

关于我应该在哪里解决这个问题的任何建议?(如果我先找到答案,我会在这里发布)

回答by BalusC

I understand that your question basically boils down to How can I include Facelets views in a JAR?

我知道您的问题基本上归结为如何在 JAR 中包含 Facelets 视图?

You can do this by placing a custom ResourceResolverin the JAR.

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

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;
    }

}

Configure this in webapp's web.xmlas follows:

在 webapp 中配置web.xml如下:

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

Imagine that you've a /META-INF/resources/foo/bar.xhtmlin random.jar, then you can just include it the usual way

想象一下你有一个/META-INF/resources/foo/bar.xhtmlin random.jar,然后你可以用通常的方式包含它

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

or even dynamically

甚至动态

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

Note: since Servlet 3.0 and newer JBoss/JSF 2.0 versions, the whole ResourceResolverapproach is not necessary if you keep the files in /META-INF/resourcesfolder. The above ResourceResolveris only mandatory in Servlet 2.5 or older JBoss/JSF versions because they've bugs in META-INFresource resolving.

注意:由于 Servlet 3.0 和更新的 JBoss/JSF 2.0 版本,ResourceResolver如果您将文件保存在文件/META-INF/resources夹中,则不需要整个方法。以上ResourceResolver仅在 Servlet 2.5 或更旧的 JBoss/JSF 版本中是强制性的,因为它们在META-INF资源解析方面存在错误。

See also:

也可以看看:

回答by nicolary

I was looking for information on the same topic and came across this link: How-to: Modular Java EE Applications with CDI and PrettyFaceswhich worked really well for me.

我正在寻找有关同一主题的信息,并发现了此链接:操作方法:带有 CDI 和 PrettyFaces 的模块化 Java EE 应用程序,这对我来说非常有效。

回答by charlie carver

By the way.. you can avoid implementing your own resource resolver when you're using seam solder (currently being integrated into apache deltaspike) which is a really useful library complementing CDI (your typical Java EE 6 component model)

顺便说一句..当您使用接缝焊料(目前已集成到 apache deltaspike)时,您可以避免实现自己的资源解析器,这是一个非常有用的库,补充了 CDI(您典型的 Java EE 6 组件模型)

I too experimented with modularity in jsf applications. Basically I built a template interface with a toolbar which gets filled with buttons provided by each module. Typically you will do this by providing a List of Strings as a Named Object:

我也在 jsf 应用程序中尝试了模块化。基本上,我构建了一个带有工具栏的模板界面,其中填充了每个模块提供的按钮。通常,您将通过提供一个字符串列表作为命名对象来做到这一点:

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

Notice how each of the buttons may come from a different module of the jsf application. The template interface contains a panel where

注意每个按钮可能来自 jsf 应用程序的不同模块。模板界面包含一个面板,其中

you can use it in your markup the following way (at your own risk ;) ) :

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

....
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>

This was the toolbar and the content panel.

这是工具栏和内容面板。

a simple button or view definition may look like this:

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

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

lets name this artifact view1.xhtml

让我们将此工件命名为 view1.xhtml

When this button is pushed (which doesn't trigger a postback using the actionListener, we want to reload the content using ajax) the switchContentMethod in your controller may change the string returned by getContentPath :

当按下此按钮时(它不会使用 actionListener 触发回发,我们想使用 ajax 重新加载内容)控制器中的 switchContentMethod 可能会更改 getContentPath 返回的字符串:

public void switchContent(){
    contentPath = "/view1.xhtml";
}

@Produces
@SomethingScoped
@Named("contentPath")
public String getContentPath(){
    return contentPath;
}

now you can change the view displayed in the panel using the button in the menubar which sort of gives you navigation without page reloads.

现在,您可以使用菜单栏中的按钮更改面板中显示的视图,这样可以在不重新加载页面的情况下进行导航。

Some advice ( or 'what I've learned' ) :

一些建议(或“我学到了什么”):

  1. You may wanna choose a large scope for the getTopMenuItems method
  2. Don't nest the ui:include tag. Unfortunately this is not possible (e.g. your view1.xhtml cannot include another composition). I really would like something like this to be possible as you can build really modular jsf views with this, kinda like portlets only without the portlets.. =D
  3. doing ui:include in container components like tabviews also proves problematic.
  4. generally it's not advisable to mix JSTL (c:forEach) and JSF. Still I found this to be the only way working as ui:repeat gets evaluated too latee.g. your included content does not appear.
  1. 您可能想为 getTopMenuItems 方法选择一个大范围
  2. 不要嵌套 ui:include 标签。不幸的是,这是不可能的(例如,您的 view1.xhtml 不能包含另一个组合)。我真的希望这样的事情成为可能,因为您可以使用它构建真正模块化的 jsf 视图,有点像没有 portlet 的 portlet .. =D
  3. 在诸如 tabviews 之类的容器组件中执行 ui:include 也证明是有问题的。
  4. 通常不建议混合使用 JSTL (c:forEach) 和 JSF。我仍然发现这是唯一的工作方式,因为 ui:repeat 被评估得太晚了,例如您包含的内容没有出现。