您所遇到的行为是完全可以预料的。此外,它与 Java 泛型的关系与 HTTP 的工作方式相同。
问题
-
HTTP 部分
问题在于您并不完全了解 HTTP 的工作原理。当您通过按下提交按钮提交数据时,您由 JSF <h:selectManyCheckbox> 标签生成的参数,作为一堆 <input type="checkbox" name="..." value="userRoleAsString"> 复选框,将被发送作为字符串和最终作为@检索987654327@ 也是字符串。当然,JSF 不知道如何构造模型对象类,Role。
-
泛型部分
正如您所知,由于 java 为泛型选择了 类型擦除 以提供向后兼容性,因此有关泛型类型的信息基本上是编译类型的工件,并且在运行时会丢失。所以在运行时你的List<Role> 会被删除为一个普通的旧List。就 EL 是一种使用 Java 反射 API 来处理您的表达式/调用方法的运行时语言而言,在运行时没有这样的信息可用。考虑到 HTTP 部分,JSF 尽其所能并将字符串对象分配给您的列表,因为这是它可以隐式执行的所有操作。如果你愿意告诉 JSF 不这样做,你需要明确地这样做,即通过指定一个转换器来知道在 HTTP 请求中期望什么类型的对象。
-
JSF 部分:后果
JSF 有一个提供的javax.faces.Enum 转换器,如果 EL 知道你的列表的编译时泛型类型,那就是Role,它确实可以工作。但它不知道。如果您在Role[] userRoles 对象上完成多项选择,或者如果您使用像<h:selectOneMenu> 中的唯一选择,并且值绑定到Role userRole,则无需提供转换器。在这些示例中,内置的枚举转换器将被自动调用。
因此,要使其按预期工作,您需要提供一个Converter,它将“解释”JSF 该列表包含什么类型的值,以及如何进行从Role 到String 的转换,反之亦然。
值得注意的是,多选 JSF 组件中的任何绑定 List<...> 值都会出现这种情况。
Stack Overflow 上的参考点
在检查并解决问题后,我想知道过去是否没有人遇到过,并在这里搜索了一些以前的答案。不出意外,之前有人问过,当然问题被BalusC解决了。以下是两个最有价值的参考点:
测试用例和工作转换器的两个示例
下面我提供了一个测试用例,完全是为了让您理解:除了第三个<h:selectManyCheckbox> 组件之外,一切都按预期工作。完全跟踪它以完全消除问题取决于您。
观点:
<h:form>
Many with enum converter
<!-- will be mapped correctly with Role object -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles}" converter="roleEnumConverter">
<f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
Many with plain converter
<!-- will be mapped correctly with Role object -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles2}" converter="roleConverter">
<f:selectItems value="#{q16433250Bean.allRoles2}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
Without any converter
<!-- will NOT be mapped correctly with Role object, but with a default String instead -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles3}">
<f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
Without any converter + array
<!-- will be mapped correctly with Role object -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles4}">
<f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
<h:commandButton value="Submit" action="#{q16433250Bean.action}"/>
</h:form>
豆子:
@ManagedBean
@RequestScoped
public class Q16433250Bean {
private List<Role> userRoles = new ArrayList<Role>();//getter + setter
private List<Role> userRoles2 = new ArrayList<Role>();//getter + setter
private List<Role> userRoles3 = new ArrayList<Role>();//getter + setter
private Role[] userRoles4;//getter + setter
public enum Role {
ADMIN("Admin"),
SUPER_USER("Super user"),
USER("User");
private final String name;
private Role(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public Role[] getAllRoles() {
return Role.values();
}
public String action() {
return null;
}
}
转换器:
@FacesConverter("roleEnumConverter")
public class RoleEnumConverter extends EnumConverter {
public RoleEnumConverter() {
super(Role.class);
}
}
和
@FacesConverter("roleConverter")
public class RoleConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null || value.equals("")) {
return null;
}
Role role = Role.valueOf(value);
return role;
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Role) || (value == null)) {
return null;
}
return ((Role)value).toString();
}
}