JSF 2.2 ViewDeclarationLanguage createComponent 将属性作为字符串传递?

JSF 2.2 ViewDeclarationLanguage createComponent passes attributes as String?

我正在尝试编写一个动态选择的 JSF 自定义组件 创建并呈现现有的 复合组件 。到目前为止,除了将属性传递给复合材料外,一切正常。

这是我的自定义组件class(错误处理等。为了更好的阅读而删除):

@FacesComponent(createTag = true)
public class ChooseEZComp extends UIComponentBase {

  @Override
  public void encodeBegin(FacesContext context) throws IOException {
    Object value = getAttributes().get("value");

    String ezCompName = value.getClass().getSimpleName().toLowerCase();
    // ezCompName is something like "freelink" or "treenode"

    Map<String, Object> params = new HashMap<>();
    params.put("node", value);
    // log.debug(params.get("node").getClass().getName()) -> yields correct class name

    ViewDeclarationLanguage viewDeclarationLanguage = context
      .getApplication()
      .getViewHandler()
      .getViewDeclarationLanguage(context, context.getViewRoot().getViewId());

    UIComponent component = viewDeclarationLanguage
      .createComponent(context,
        "http://xmlns.jcp.org/jsf/composite/ezcomp",
        ezCompName,
        params);

    component.encodeAll(context);
  }
}

一个复合组件(我有几个),由这个 class:

选择和渲染
  <cc:interface>
    <cc:attribute name="node" required="true"/>
  </cc:interface>

  <cc:implementation>
    <h:outputText value="The class of node is: #{cc.attrs.node.class.name}"/>
  </cc:implementation>

这是我在 JSF 页面中使用标签的方式:

<test:chooseEZComp value="#{treeView.selectedNode.data}"/>

所以 "value" 总是保证 not 类型 java.lang.String (它是一些 JPA @Entity)。

但是 JSF 页面中的结果输出始终是:

The class of node is: java.lang.String

我哪里错了?是否可以将 String 以外的其他内容作为参数传递给复合材料?

我是 runnind wildfly-8.2.0-final Java EE 7(和 Primefaces 5,但此处未使用)

欢迎任何提示!


编辑:当然我也尝试在cc:interface

中强制属性的类型
<cc:interface>
  <cc:attribute name="node" required="true" type="some.package.type"/>
</cc:interface>

但这因此导致了 IllegalArgument 异常:

IllegalArgumentException: Cannot convert ... of type class java.lang.String to class

原来我误解了签名中的 API ... Map 让我认为我可以传递一个对象。但是 Javadoc 对此更加精确:

attributes - any name=value pairs that would otherwise have been given on the markup that would cause the creation of this component [..]

因此,您不会将值传递给 createComponent,而是传递 用于计算指定属性值的表达式或 属性 name:

  Map<String, Object> attributes = new HashMap<>();
  ValueExpression valueExpression = getValueExpression("value");
  attributes.put("node", valueExpression.getExpressionString());

有趣的一面阅读: 找到一个解决方案,我通过 jsf-imp-2.2.8-jbossorg 调试并偶然发现了创建组件的代码。基本上它的作用是:

  • 在临时文件夹中创建一个 JSF xhtml 文件并使用 OutputStreamWriter#append 编写一个 JSF 页面,其中只有一个标签(您要创建的标签)
  • 遍历所有属性并将它们作为属性写入标签
  • 保存文件并将其提供给 DefaultFaceletFactory#createFacelet
  • 创建命名容器并使其成为生成的 facelet 的父级(应用)
  • 在命名容器上使用 findComponent 获取生成的标签并return它

至少在找到这个之后就清楚为什么你需要传入值表达式而不是值本身了。