我认为我在使用JSF 2.0(使用Primefaces)的Java运行时发现了一个错误,在这个项目中我正在使用JSF 2.0 Primefaces和CDI。
恢复问题,我的业务类Role中有一个方法设置器,它接收一个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
当我尝试在这样的foreach中使用时:
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的错误?或者我被误解了?
首先我有一个具有p:selectManyCheckbox
的UI JSF(.xhtml),这个manyCheckbox从managedbean接收一个枚举PontoSenha数组(使用enum的方法值())并将值定义为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);
}
}
ManagedBean的声明
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
答案 0 :(得分:12)
这是由多种技术限制和事实相结合引起的。
在Java中,泛型是编译时语法糖。最终,当编译Java类时,所有泛型类型信息都将丢失。因此,在运行时期间,传递List
实例中的任何泛型类型信息都无法实现。
表达式语言(EL,#{}
)在运行时使用Java Reflection API运行,在您的特定情况下只能看到List
,而不是List<Integer>
。
生成的HTML输出和获取的HTTP请求参数基本上是Java透视图String
s。
只要您没有在String
之间明确指定所需类型的JSF Converter
,JSF就会让EL(读取:Reflection API)添加未转换的提交{{1} } {}到String
的值。
为了使提交的值最终在模型中List
,您有2个选项:
明确指定Integer
到String
的转换器。幸运的是,JSF有一个内置的IntegerConverter
,其转换器ID为javax.faces.Integer
。所以你需要做的就是在输入组件的Integer
属性中指定它。
converter
使用<p:selectManyCheckbox ... converter="javax.faces.Integer">
代替Integer[]
作为模型属性。
List<Integer>
这样,EL可以看到所需的类型(读取:Reflection API),它将使用内置转换器执行自动转换。
升级到至少JSF 2.3。根据{{3}},private Integer[] acl;
组件在使用UISelectMany
时会自动转换,使用与spec issue 1422相同的基本原则。
另见OmniFaces SelectItemsConverter
。
使用以下算法获取
Collection
:
如果该组件附加了
Converter
,请使用它。如果没有,请查找值
Converter
(如果有)。ValueExpression
必须指向以下内容:
基元数组(例如
ValueExpression
)。查找已注册的类int[]
以获取此原始类型。一组对象(例如
Converter
或Integer[]
)。查找已注册的基类String[]
以获取基础元素类型。A
Converter
。不要转换值。相反,将提供的可用选项集转换为字符串,与呈现响应期间完全相同,并且对于与提交的值的任何匹配,将可用选项作为对象添加到集合中。如果由于某种原因无法找到
java.util.Collection
,则假定该类型为String数组。