【问题标题】:List<Integer> received List<String>List<Integer> 收到 List<String>
【发布时间】:2014-02-22 03:22:35
【问题描述】:

我认为我在使用 JSF 2.0(使用 Primefaces)的 Java 运行时发现了一个错误,在这个项目中我使用的是 JSF 2.0 Primefaces 和 CDI。

恢复问题,我的业务类角色中有一个方法设置器,它接收到一个列表,但 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);
    }
}

这个方法的输出是:

收到:[1, 5] 类 java.lang.String > 1 类 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 上的错误吗?还是我的理解有误?

首先我有一个 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);
    }
}

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

【问题讨论】:

  • 您应该真正减少代码并专注于基础知识。我试图编辑语法突出显示,但在 8 个条目后放弃了。
  • 对此感到抱歉。我也尝试过编辑,但 XHTML 没有格式化

标签: jsf-2 primefaces cdi


【解决方案1】:

这是由多种技术限制和事实共同造成的。

  • 在 Java 中,泛型是编译时语法糖。最终,当一个 Java 类被编译时,所有的泛型类型信息都会丢失。因此,在运行时期间,List 实例中没有任何泛型类型信息被传递。

  • 表达式语言(EL,#{})在运行时使用 Java 反射 API 运行,在您的特定情况下只看到 List,而不是 List&lt;Integer&gt;

  • 生成的 HTML 输出和获取的 HTTP 请求参数在 Java 角度基本是Strings。

  • 只要您没有在String 与所需类型之间显式指定JSF Converter,JSF 就会让EL(阅读:反射API)将未转换的提交String 值添加到List

为了使提交的值在模型中以 Integer 结尾,您有 3 个选项:

  1. 明确指定StringInteger 的转换器。幸运的是,JSF 有一个内置的 IntegerConverter,它的转换器 ID 为 javax.faces.Integer。所以你需要做的就是在输入组件的converter 属性中指定它。

     <p:selectManyCheckbox ... converter="javax.faces.Integer">
    
  2. 使用Integer[] 代替List&lt;Integer&gt; 作为模型属性。

     private Integer[] acl;
    

    这样,所需的类型对 EL 可见(阅读:反射 API),它将使用内置转换器执行自动转换。

  3. 至少升级到 JSF 2.3。根据spec issue 1422UISelectMany 组件在使用Collection 时会自动转换,使用与OmniFaces SelectItemsConverter 相同的基本原理。

另见UISelectMany javadoc

使用以下算法获取Converter

  • 如果组件有一个附加的Converter,使用它。

  • 如果没有,请查找 ValueExpression 的值(如果有)。 ValueExpression 必须指向以下内容:

    • 基元数组(例如int[])。查找已注册的类 Converter 以获取此原始类型。

    • 对象数组(例如Integer[]String[])。为底层元素类型查找注册的类Converter

    • java.util.Collection。不要转换值。相反,将提供的一组可用选项转换为字符串,就像在渲染响应期间所做的那样,并且对于与提交值的任何匹配,将可用选项作为对象添加到集合中。

如果由于某种原因找不到Converter,则假定类型为字符串数组。

【讨论】:

  • 好的,我明白了。非常感谢您!!但是关于运行时的反射是不是很糟糕,不是吗?我的意思是我可以使用一个使用泛型集合的框架,我可能会错误地使用它传递不正确的集合,也许有办法破坏经常使用它的应用程序。
  • 不确定您所说的“非常糟糕”是什么意思。也许您已经考虑在 i586 上运行 Java 1.2?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 2022-09-30
  • 2017-01-28
  • 2017-07-18
  • 2012-05-29
相关资源
最近更新 更多