我认为我在使用 JSF 2.0(使用 Primefaces)的 Java 运行时发现了一个错误,在这个项目中我使用了 JSF 2.0 Primefaces 和 CDI。
继续这个问题,我的业务类 Role 中有一个方法 setter 接收了一个 List,但 JSF 正在其上设置一个 ArrayList。 java应该抛出异常还是至少不应该找到匹配的方法? 这是:
public void setAcl(List<Integer> acl) {
this.acl = acl;
System.out.println("Received: " + this.acl);
for(Object i : this.acl) {
System.out.println(i.getClass() + " > " + i);
}
}
该方法的输出是:
Received: [1, 5] class java.lang.String > 1 class java.lang.String > 5
And when I try to use in foreach like this:
for(Integer i : this.acl) {
System.out.println(i.getClass() + " > " + i);
}
抛出
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
你能解释一下这是怎么回事吗?这是 JSF 或 Java 上的错误吗?还是我的理解有误?
首先,我有一个 UI JSF (.xhtml),它有一个 p:selectManyCheckbox
,这个 ManyCheckbox 从 ManagedBean 接收枚举 PontoSenha 数组(使用枚举的方法 Values() )并定义值到 itemValue 和 itemLabel。
遵循manyCheckbox的代码:
<p:selectManyCheckbox id="acl" value="#{roleController.role.acl}" layout="grid" columns="4" required="true" requiredMessage="Selecione pelo menos um ponto de senha" >
<f:selectItems value="#{roleController.pontosSenha}" var="pontoSenha" itemValue="#{pontoSenha.pontoSenha}" itemLabel="#{pontoSenha.descricao}" />
</p:selectManyCheckbox>
枚举 PontoSenha 声明:
package br.com.bsetechnology.atacadao.business;
public enum PontoSenha {
CADASTRO_FORNECEDOR(1, "Cadastrar fornecedor"),
CADASTRO_LOJA(2, "Cadastrar loja"),
CADASTRO_PRODUTO(3, "Cadastrar produto"),
RELATORIO(4, "Gerar relatório"),
SEGURANCA_GRUPOS(5, "Gerenciar grupos de usuário"),
SEGURANCA_USUARIOS(6, "Gerenciar usuários");
private int pontoSenha;
private String descricao;
private PontoSenha(int pontoSenha, String descricao) {
this.pontoSenha = pontoSenha;
this.descricao = descricao;
}
public int getPontoSenha() {
return pontoSenha;
}
public String getDescricao() {
return descricao;
}
@Override
public String toString() {
return String.format("%02d - %s", pontoSenha, descricao);
}
}
托管Bean的声明
package br.com.bsetechnology.atacadao.controller;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.inject.Inject;
import javax.inject.Named;
import br.com.bsetechnology.atacadao.business.PontoSenha;
import br.com.bsetechnology.atacadao.business.Role;
import br.com.bsetechnology.atacadao.core.FacesUtil;
import br.com.bsetechnology.atacadao.dao.RoleDAO;
@Named("roleController")
@RequestScoped
public class RoleController {
@Inject
private RoleDAO roleDao;
private Role role;
public void setRoleDao(RoleDAO roleDao) {
this.roleDao = roleDao;
}
public Role getRole() {
if(role == null)
role = new Role();
return role;
}
public void setRole(Role role) {
this.role = role;
}
public PontoSenha[] getPontosSenha() {
return PontoSenha.values();
}
public List<Role> getAll() {
return roleDao.getAll();
}
public void salva() {
System.out.println("Salvando");
boolean resultAction = false;
if(role.getCodigo() > 0) {
resultAction = roleDao.update(role);
} else {
resultAction = roleDao.insert(role);
}
if(resultAction) {
role = new Role();
FacesUtil.addMessage(FacesMessage.SEVERITY_INFO, "Grupo salvo com sucesso.", null);
} else {
FacesUtil.addMessage(FacesMessage.SEVERITY_WARN, "Grupo não foi salvo.", null);
}
}
}
商务舱角色
package br.com.bsetechnology.atacadao.business;
import java.util.ArrayList;
import java.util.List;
public class Role {
private int codigo;
private String descricao;
private List<Integer> acl;
public Role() {
acl = new ArrayList<Integer>();
}
public Role(int codigo, String descricao, List<Integer> acl) {
setCodigo(codigo);
setDescricao(descricao);
setAcl(acl);
}
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public List<Integer> getAcl() {
return acl;
}
public void setAcl(List<Integer> acl) {
this.acl = acl;
System.out.println("Received: " + this.acl);
for(Object i : this.acl) {
System.out.println(i.getClass() + " > " + i);
}
}
public void addPontoSenha(int pontoSenha) {
this.acl.add(pontoSenha);
}
public void remPontoSenha(int pontoSenha) {
this.acl.remove(pontoSenha);
}
}
我用来注册角色的页面 JSF(template.xhtml 只是 HTML 和 CSS):
<ui:composition template="/WEB-INF/template.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
<ui:define name="body-content">
<div class="row">
<h:form id="formGrupo" class="form-horizontal"><fieldset>
<p:panel header="Edição de grupo de usuário" >
<div class="control-group">
<h:outputLabel for="codigo" value="Código" readonly="" styleClass="control-label" />
<div class="controls">
<h:inputText id="codigo" class="form-control" value="#{roleController.role.codigo}"
style="width:100px;text-align:right;" />
</div>
</div>
<div class="control-group">
<h:outputLabel for="descricao" value="Descrição" styleClass="control-label" />
<div class="controls">
<h:inputText id="descricao" class="form-control" value="#{roleController.role.descricao}"
required="true" maxlength="20" requiredMessage="Descrição obrigatória" converterMessage="Descrição inválida" />
<h:message for="descricao" class="error" />
</div>
</div>
<div class="control-group">
<h:outputLabel value="Pontos de senha" styleClass="control-label" />
<div class="controls checkbox">
<p:selectManyCheckbox id="acl" value="#{roleController.role.acl}" layout="grid" columns="4"
required="true" requiredMessage="Selecione pelo menos um ponto de senha" >
<f:selectItems value="#{roleController.pontosSenha}" var="pontoSenha" itemValue="#{pontoSenha.pontoSenha}" itemLabel="#{pontoSenha.descricao}" />
</p:selectManyCheckbox>
<h:message for="acl" errorClass="error" />
</div>
</div>
<div class="form-actions">
<hr/>
<p:commandLink value="Salvar" styleClass="btn btn-primary" style="color:#fff;" action="#{roleController.salva}" update=":formGrupo,:formTable:tblRoles">
</p:commandLink>
</div>
<p:messages globalOnly="true" showDetail="false" closable="true" />
</p:panel>
</fieldset></h:form>
</div>
<br/>
<div class="row">
<h:form id="formTable" styleClass="form-horizontal"><fieldset>
<p:panel header="Grupos de usuários">
<p:dataTable id="tblRoles" var="role" value="#{roleController.all}" rowKey="#{role.codigo}" stickheader="true" >
<p:column headerText="Código" width="20">
<h:outputText value="#{role.codigo}" />
</p:column>
<p:column headerText="Descrição">
<h:outputText value="#{role.descricao}" />
</p:column>
<p:column width="200">
<p:commandLink value="Editar" class="btn btn-success btn-sm" style="color:#fff;margin-left:5px;" update=":formGrupo">
<f:setPropertyActionListener value="#{role}" target="#{roleController.role}" />
</p:commandLink>
<p:commandLink value="Excluir" styleClass="btn btn-danger btn-sm" style="color:#fff;margin-left:5px;" />
</p:column>
</p:dataTable>
</p:panel>
</fieldset></h:form>
</div>
</ui:define>
</ui:composition>
完整打印堆栈跟踪
Jan 29, 2014 10:59:43 PM com.sun.faces.context.AjaxExceptionHandlerImpl handlePartialResponseError
SEVERE: javax.faces.component.UpdateModelException: javax.el.ELException: /seguranca/grupos.xhtml @27,87 value="#{roleController.role.acl}": Error writing 'acl' on type br.com.bsetechnology.atacadao.business.Role
at javax.faces.component.UIInput.updateModel(UIInput.java:867)
at javax.faces.component.UIInput.processUpdates(UIInput.java:749)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at org.primefaces.component.panel.Panel.processUpdates(Panel.java:288)
at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1254)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at br.com.bsetechnology.atacadao.core.AccessControlFilter.doFilter(AccessControlFilter.java:31)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.el.ELException: /seguranca/grupos.xhtml @27,87 value="#{roleController.role.acl}": Error writing 'acl' on type br.com.bsetechnology.atacadao.business.Role
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:139)
at javax.faces.component.UIInput.updateModel(UIInput.java:832)
... 30 more
Caused by: javax.el.ELException: Error writing 'acl' on type br.com.bsetechnology.atacadao.business.Role
at javax.el.BeanELResolver.setValue(BeanELResolver.java:153)
at com.sun.faces.el.DemuxCompositeELResolver._setValue(DemuxCompositeELResolver.java:255)
at com.sun.faces.el.DemuxCompositeELResolver.setValue(DemuxCompositeELResolver.java:281)
at org.apache.el.parser.AstValue.setValue(AstValue.java:218)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:253)
at org.jboss.weld.el.WeldValueExpression.setValue(WeldValueExpression.java:64)
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:131)
... 31 more
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at br.com.bsetechnology.atacadao.business.Role.setAcl(Role.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at javax.el.BeanELResolver.setValue(BeanELResolver.java:142)
... 37 more
最佳答案
这是由多种技术限制和事实共同造成的。
在 Java 中,泛型是编译时语法糖。最终,当编译 Java 类时,所有泛型类型信息都会丢失。因此,在运行时期间,
List
中无法获取任何通用类型信息。正在传递的实例。表达式语言(EL,
#{}
)在运行时使用 Java Reflection API 运行,并且在您的特定情况下仅看到List
,不是List<Integer>
.生成的HTML输出和获取的HTTP请求参数基本上都是Java视角的
String
s。只要您没有显式指定 JSF
Converter
之间String
对于所需的类型,JSF 会让 EL(读:Reflection API)添加未转换的提交String
值List
.
为了让提交的值最终成为 Integer
在模型中,您有 3 个选项:
显式指定
String
的转换器至Integer
。幸运的是,JSF 有一个内置的IntegerConverter
其转换器 IDjavax.faces.Integer
。因此,您需要做的就是在输入组件的converter
中指定它。属性。<p:selectManyCheckbox ... converter="javax.faces.Integer">
使用
Integer[]
而不是List<Integer>
作为模型属性。private Integer[] acl;
这样,所需的类型对于 EL 是可见的(阅读:Reflection API),并且它将使用内置转换器执行自动转换。
至少升级到 JSF 2.3。根据spec issue 1422
UISelectMany
当Collection
时,组件将自动转换使用与 OmniFacesSelectItemsConverter
相同的基本原理.
另请参阅 UISelectMany
javadoc .
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). TheValueExpression
must point to something that is:
An array of primitives (such as
int[]
). Look up the registered by-classConverter
for this primitive type.An array of objects (such as
Integer[]
orString[]
). Look up the registered by-classConverter
for the underlying element type.A
java.util.Collection
. Do not convert the values. Instead, convert the provided set of available options to string, exactly as done during render response, and for any match with the submitted values, add the available option as object to the collection.If for any reason a
Converter
cannot be found, assume the type to be a String array.
关于jsf-2 - List<Integer> 收到 List<String>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21446068/