JSF 2.2:将 bean 属性作为 ValueExpression 传递给复合

JSF 2.2: Passing bean attribute as ValueExpression to composite

我想将 non-managed (non-String) object 作为属性传递给动态添加的复合组件,并让它在 session 之后继续存在。

JSF 2.2 ViewDeclarationLanguage#createComponent 处理复合组件的动态 non-string 属性值的方式不同于旧的 Mojarra 依赖代码 (Application#createComponent)。我找不到完全适用于 JSF 2.2 技术的方法,但可能是我。

[我正在尝试通过转换为 MyFaces 来移除 Mojarra 依赖项(并解决其他一些 Mojarra 问题)。我正在使用 JSF 2.2、CDI Weld 2.2.16、Tomcat v8.0]

我正在以编程方式实例化不同的复合组件(注意 bean 属性):

<cc:interface>
    <cc:attribute name="bean" required="true" type="com.aadhoc.cvc.spikes.extensionsapproach.ExtensionBeanInterface"/>
</cc:interface>

<cc:implementation>
    <h:panelGrid columns="2">
        <h:outputText value="Title:"/>
        <h:inputText value="#{cc.attrs.bean.title}"/>
    </h:panelGrid>
</cc:implementation>

在旧的 Mojarra 依赖方法中,我实例化了 non-managed bean object,并将其作为属性直接添加到复合组件中,效果很好(我使用的是@BalusC 的很棒但来自 OmniFaces Component#includeCompositeComponent):

的依赖于 Mojarra 的示例代码
ExtensionBeanInterface bean = Class.forName(className).newInstance();
attributes = new HashMap<String, Object>();
attributes.put("bean", bean); // Using bean object itself

[..]
UIComponent composite = application.createComponent(context, resource);
composite.getAttributes().putAll(attributes);
[..]

在 JSF 2.2 中,我发现我必须直接传递字符串 ValueExpression 而不是我的 bean object。我目前正在使用这种技术,但不能完全正确:

FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();

ValueExpression beanValExp = context.getApplication().getExpressionFactory()
            .createValueExpression(elContext, "#{customBeanVE}", ExtensionBeanInterface.class);
beanValExp.setValue(elContext, bean);
String beanValExpStr = beanValExp.getExpressionString();

attributes = new HashMap<String, Object>();
attributes.put("bean", beanValExpStr); // Using VE instead of bean object

UIComponent composite = context.getApplication().getViewHandler()
                .getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
                .createComponent(context, taglibURI, tagName, attributes);
[..]

这在第一个 "add composite" 上效果很好,但在提交任何后续表单时,我得到:

/resources/com/aadhoc/cvc/spikes/extensionsapproach/components/House.xhtml @16,49 value="#{cc.attrs.bean.title}": Target Unreachable, 'bean' returned null

我已验证组合的 required 和 type 属性工作正常,并且 #{cc.attrs.bean.title} 最初显示了 bean 的标题。我通过静态使用刷新工作正常的复合组件进行了验证。

怎么回事,我怎样才能移交 bean object 以便它与复合体一起在 session 中存活?

我在 Mojarra 的这个工作很棒。我可以将 bean 对象放在属性值中,一切都很棒。尝试 MyFaces,我需要 change/update 我的方法,现在我需要使用 EL 字符串而不是直接对象引用。

由于所有人都只是将 bean 对象放入属性 Map 中,所以我想要一个神奇而优雅的地方来放置 bean 对象并让它们存活下来。我本可以将它们放入 "global" 会话映射(像这样:How to save an object into JSF Session),但它不够干净。然后我将它们放入我的一个主要会话状态 bean (modelerBean) 中,它是正确的。这就是我保存 bean 的方式,以及我如何使用 EL 字符串指向它的方式。无需创建 ValueExpression 或 MethodExpression 对象或注册特殊映射。这种与 JSF 2.2 兼容的方法在 Mojarra 和 MyFaces 中对我都有效。

public void onAdd(ActionEvent ev) {
    [..]
    ChosenBean chosenBean = new ChosenBean();
    chosenBean.setComponentId(id);
    chosenBean.setBean(bean);
    modelerBean.addChosen(chosenBean);
    [..]
    String el = "#{modelerBean.getChosen('"+id+"').bean}"
    attributes.put(MODELER_EXTENSION_BEAN_ATTRIBUTE_NAME, el);
    [..]

我在阅读@BalusC 的 post 后决定这样做:How do I get and set an object in session scope in JSF?

请注意,我对@BalusC 的两种 "add composites dynamically" 方法 (How to programmatically or dynamically create a composite component in JSF 2) 的经验是,如果可以的话,您绝对应该使用 JSF 2.2 方法。如果您不在 JSF 2.2 中,旧的 Mojarra 方法确实有效。一旦我修改了我的代码以使 JSF 2.2 方法起作用,旧的 Mojarra 方法就会以奇怪的方式崩溃。