何时在 JSF 中命名转换器

When to name a converter in JSF

我一直在我的 PrimeFaces SelectOneMenu 对象中使用转换器。如果我只告诉转换器指的是哪个class,它们就可以正常工作:

@FacesConverter(forClass = DescriptorVO.class)
public class DescriptorVOConverter implements Converter { ... }

这样,我不必明确告诉 JSF 组件在填充 class DescriptorVO.

的对象时应该使用哪个转换器

但是,我创建了一个使用 p:SelectManyCheckbox 的页面,但我终究无法知道为什么它没有调用我的转换器。然后我给它起了个名字,像这样:

@FacesConverter(forClass = RoleVO.class, value = "roleVOConverter")
public class RoleVOConverter implements Converter { ... }

并将其作为组件的属性之一传递

<p:selectManyCheckbox id="cbx_roles" required="true" converter="roleVOConverter"
    requiredMessage="At least one role must be selected."
    value="#{userView.selectedRoles}" layout="responsive">
    <f:selectItems value="#{userView.roles}" var="role"
        itemLabel="#{role.title}" itemValue="#{role}" />
</p:selectManyCheckbox>

voi la,它开始正确调用转换器。这向我提出了一个问题,关于何时我应该命名我的转换器(通过 value 属性)以及何时告诉他们转换器应该与哪个 class 一起使用(使用 forClass 属性)是足够。在使用 PrimeFaces 时,我从来不需要命名任何转换器,只是为了这个特定的 SelectManyCheckbox 组件。不同的组件是否对转换器有不同的要求,还是我只是误解了转换器的概念?

UISelectMany 组件的 value 引用通用 java.util.Collection 类型(例如 List<Role> 而不是数组 Role[] 时,就会发生这种情况。这在 javadoc of UISelectMany 中指定(强调我的):

Obtain the Converter using the following algorithm:

  • If the component has an attached Converter, use it.

  • If not, look for a ValueExpression for value (if any). The ValueExpression must point to something that is:

    • An array of primitives (such as int[]). Look up the registered by-class Converter for this primitive type.
    • An array of objects (such as Integer[] or String[]). Look up the registered by-class Converter for the underlying element type.
    • A java.util.Collection. Do not convert the values.
  • If for any reason a Converter cannot be found, assume the type to be a String array.

原因是,泛型类型 <Role> 在运行时丢失,无法直接确定。当您使用 MyFaces instead of Mojarra 时它会起作用,因为它会在 java.util.Collection 的情况下通过手动迭代 <f:selectItem(s)> 检查实际类型并根据第一个非 - 确定 Converter null 值。

我为此创建了规范 issue 1422 以在 JSF 规范中获得它并改进 Mojarra。

另请参阅:

  • UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T
  • Why does JSF put String values in a Map<..., Integer>? And how to work around it?
  • Use enum in h:selectManyCheckbox