You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PrimeFaces PickList在JBoss EAP 7.2渲染时出现NullPointerException问题

PrimeFaces PickList 触发NullPointerException:DualListModel始终为null的问题排查

我碰到了和PrimeFaces PickList搭配OmniFaces validateAll时触发NPE类似的问题,调试PickListRenderer时出现完全一致的症状。虽然Thomas Andraschko曾认为这是Mojarra的问题,但我切换到MyFaces 2.2和2.3版本后,错误依然存在。

已尝试的修复方案

  • 替换Mojarra为MyFaces
  • 将PrimeFaces从7.0升级到8.0.RC1
  • 添加自定义PickListConverter
  • 通过jboss-deployment-structure.xml禁用JBoss自带的相关依赖包

错误触发细节

错误在页面加载时直接触发,添加Converter是我尝试的修复手段,但实际上错误在Converter生效前就已出现——无论是否配置Converter,都会抛出NPE。

我的环境是:JBoss EAP 7.2,代码仓库地址为https://github.com/Qleoz12/Primefaces-Mydemo

错误详情

始终抛出java.lang.NullPointerException,根源在org.primefaces.component.picklist.PickListRenderer.encodeMarkup(PickListRenderer.java:103)。调试发现encodeMarkup方法中的DualListModel model始终为null,调用encodeList时触发空指针,核心堆栈信息如下:

java.lang.NullPointerException
at org.primefaces.component.picklist.PickListRenderer.encodeMarkup(PickListRenderer.java:103)
at org.primefaces.component.picklist.PickListRenderer.encodeEnd(PickListRenderer.java:74)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:919)
...

相关代码

XHTML页面代码

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml" 
                xmlns:ui="http://java.sun.com/jsf/facelets" 
                xmlns:h="http://java.sun.com/jsf/html" 
                xmlns:f="http://java.sun.com/jsf/core" 
                xmlns:p="http://primefaces.org/ui" 
                xmlns:c="http://java.sun.com/jsp/jstl/core" 
                xmlns:ccp="http://java.sun.com/jsf/composite/cc" 
                template="../template/ui.xhtml">
<ui:define name="body">
<p:pickList id="FF" 
            value="#{CompositeComponent.cities}" 
            var="cities" 
            itemLabel="#{cities}" 
            itemValue="#{cities}" 
            converter="PickListConverter" >
</p:pickList>
</ui:define>
</ui:composition>

Bean代码

package Beans;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Named;
import org.primefaces.model.DualListModel;

@Named
@javax.faces.view.ViewScoped
public class CompositeComponent implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CompositeComponent.class);
    
    private DualListModel<String> cities;
    List<String> citiesSource = new ArrayList<String>();
    List<String> citiesTarget = new ArrayList<String>();

    public CompositeComponent() { super(); }

    @PostConstruct
    public void init() {
        citiesSource.add("San Francisco");
        citiesSource.add("London");
        citiesSource.add("Paris");
        citiesSource.add("Istanbul");
        citiesSource.add("Berlin");
        citiesSource.add("Barcelona");
        citiesSource.add("Rome");
        cities = new DualListModel<String>(citiesSource, citiesTarget);
    }

    public DualListModel<String> getCities() { return cities; }
    public void setCities(DualListModel<String> cities) { this.cities = cities; }
}

自定义Converter代码

package converter;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import org.primefaces.component.picklist.PickList;
import org.primefaces.model.DualListModel;

@SuppressWarnings({"unused", "rawtypes"})
@FacesConverter("PickListConverter")
public class PickListConverter implements Converter{

    public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
        PickList p = (PickList) component;
        DualListModel dl = (DualListModel) p.getValue();
        return dl.getSource().get(Integer.valueOf(submittedValue));
    }

    public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
        PickList p = (PickList) component;
        DualListModel dl = (DualListModel) p.getValue();
        return String.valueOf(dl.getSource().indexOf(value));
    }
}

问题分析与可能的解决方案

从调试结果来看,PickListRenderer渲染时无法获取DualListModel实例,说明JSF没有正确将Bean中的cities属性绑定到组件上,核心可能原因如下:

1. EL表达式的Bean名称错误

JSF的@Named注解默认会将类名首字母小写作为Bean名称,也就是#{compositeComponent.cities},而不是XHTML中使用的大写开头#{CompositeComponent.cities}。这很可能是导致组件无法找到Bean属性的关键原因。

  • 解决方案:将XHTML中的value="#{CompositeComponent.cities}"改为value="#{compositeComponent.cities}",或者在@Named注解中显式指定名称:@Named("CompositeComponent")。

2. 视图作用域初始化异常

虽然使用了@PostConstruct初始化cities,但可能组件渲染时Bean还未完成初始化,或者视图作用域的生命周期出现异常。

  • 解决方案:临时切换到@SessionScoped验证是否是视图作用域的问题,同时确保项目依赖完整支持JSF 2.2+的视图作用域。

3. Converter空值处理缺失

即使当前错误不在Converter,也应该添加空值判断避免后续潜在的NPE:

public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
    if (submittedValue == null || submittedValue.isEmpty()) {
        return null;
    }
    PickList p = (PickList) component;
    DualListModel dl = (DualListModel) p.getValue();
    if (dl == null || dl.getSource() == null) {
        return null;
    }
    int index = Integer.parseInt(submittedValue);
    return index >= 0 && index < dl.getSource().size() ? dl.getSource().get(index) : null;
}

public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
    if (value == null) {
        return "";
    }
    PickList p = (PickList) component;
    DualListModel dl = (DualListModel) p.getValue();
    return dl != null && dl.getSource() != null ? String.valueOf(dl.getSource().indexOf(value)) : "";
}

内容的提问来源于stack exchange,提问作者qleoz12

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:10:50