Java JSF与类型安全
当我挣扎了几个小时后,我终于找到了那些恼人的Java JSF与类型安全,java,jsf,generics,classcastexception,type-safety,Java,Jsf,Generics,Classcastexception,Type Safety,当我挣扎了几个小时后,我终于找到了那些恼人的ClassCastExceptions的来源,我认为它们是由Hibernate生成的,并且是enum-映射 但是它们来自我的JSF视图,在那里我从 <h:selectManyCheckbox value="#{createUserManager.user.roles}" ... > <f:selectItems value="#{createUserManager.roles}"/> </
ClassCastException
s的来源,我认为它们是由Hibernate生成的,并且是enum
-映射
但是它们来自我的JSF视图,在那里我从
<h:selectManyCheckbox value="#{createUserManager.user.roles}" ... >
<f:selectItems value="#{createUserManager.roles}"/>
</h:selectManyCheckbox>
将列表参数角色
更改为列表参数角色
效果很好。这怎么可能?那些泛型不应该是类型安全的吗?或者与JSF相关的类型擦除会扼杀整个类型安全吗?
h:selectManyCheckbox
的返回值不应该是List
,就像我通过f:selectItems
传入的那样吗?您所经历的行为完全是意料之中的。此外,它与java泛型的关系与HTTP的工作方式相同
问题
标记生成的参数作为一组
复选框,将作为字符串发送,并最终作为request.getParameter(“checkboxName”)检索代码>也作为字符串
。当然,JSF不知道如何构造模型对象类,Role
列表
将擦除为一个普通的、好的列表
。至于EL是一种运行时语言,它使用Java反射API来处理表达式/调用方法,在运行时没有此类信息可用。考虑到HTTP部分,JSF尽其所能将字符串对象分配给列表,这是它所能隐式完成的全部工作。如果您愿意告诉JSF不要这样做,则需要显式地这样做,即通过指定转换器来了解HTTP请求中预期的对象类型javax.faces.Enum
转换器,如果EL知道列表的编译时泛型类型,那么它确实可以工作,即Role
。但它不知道。如果在角色[]用户角色
对象上进行多个选择,或者使用像
中那样的唯一选择并将值绑定到角色
,则无需提供转换器。在这些示例中,将自动调用内置的枚举转换器
因此,为了让它按预期工作,您需要提供一个“解释”JSF这个列表包含什么类型的值,以及如何从角色
转换到字符串
,反之亦然
值得注意的是,对于多选JSF组件中的任何绑定的List
值,情况都是如此堆栈溢出的参考点 在问题被检查和解决后,我想知道过去是否没有人面对它,并在这里寻找以前的答案。毫不奇怪,以前有人问过这个问题,当然这个问题是由巴卢斯克解决的。以下是两个最有价值的参考点:
- )李>
- )李>
测试用例和两个工作转换器示例 下面我提供了一个完全供您理解的测试用例:除了第三个
组件之外,其他一切都按预期工作。这取决于您如何全面跟踪,以彻底消除该问题
观点:
<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>
及
我不了解JSF,所以我不能很好地推测,但是JSF对泛型的支持看起来非常糟糕:标准enum转换器在JSF2.0中不应该在没有明确设置的情况下这样做吗?不,在这种特殊情况下不应该这样做。请看上面的扩展答案。我知道转换器一般是如何工作的(以及如何像我已经做的那样编写它们),但我错了,因为我认为标准enum转换器可以在不做任何额外工作的情况下为我完成这项工作。不过,这个答案与BalusC类似,谢谢:)Java枚举不需要转换器。
<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();
}
}