Spring整合JSF+PrimeFaces时RowEdit事件监听器未触发问题排查
Spring + JSF 集成PrimeFaces可编辑表格RowEdit事件失效问题排查
问题现场

项目核心代码
实体类 Game.java
package org.xtremebiker.jsfspring.model; import org.springframework.stereotype.Component; import java.util.Date; @Component(value = "Game") public class Game { private int id; private String name; private int budget; private int profit; private int number; private int genre; private int platform; private int status; private Date start; private Date end; // 构造方法、getter/setter 已省略 public void update(Game new_game) { this.name = new_game.name; this.budget = new_game.budget; this.profit = new_game.profit; this.number = new_game.number; this.genre = new_game.genre; this.platform = new_game.platform; this.status = new_game.status; this.start = new_game.start; this.end = new_game.end; } }
页面托管Bean Games.java
package org.xtremebiker.jsfspring.view; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.primefaces.event.RowEditEvent; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.SessionScope; import org.xtremebiker.jsfspring.model.Game; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; @Component(value = "games") @SessionScope public class Games { private List<Game> All_games = new ArrayList<Game>(); public Games() { System.out.println("Created!"); All_games.add(new Game(1, "One", 1000, 500, 8, 1, 2, 4, new Date(), new Date())); } public void onRowEdit(RowEditEvent event) { Game new_game = (Game) event.getObject(); All_games.get(new_game.getId()).update(new_game); FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "INFO", "X/Y edit successful!"); FacesContext.getCurrentInstance().addMessage(null, msg); } public List<Game> getAll_games() { return All_games; } public void setAll_games(List<Game> all_games) { All_games = all_games; } }
视图页面 hello.xhtml
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:p="http://primefaces.org/ui" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> <h:head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"/> <style> .container { display: flex; justify-content: space-between; width: 100%; } </style> </h:head> <h:body> <div class="container"> <nav id="sidebarMenu" style="padding-right: 5px; flex: 1 1 auto;"> <p:menu toggleable="true"> <p:submenu label="Игры"> <p:menuitem value="Список игр" url="http://www.youtube.com" /> <p:menuitem value="Добавить игру" url="http://www.youtube.com" /> </p:submenu> <p:menuitem value="Список сотрудников" url="http://www.youtube.com" /> <p:menuitem value="Список кандидатов" url="http://www.youtube.com" /> </p:menu> </nav> <div style="flex: 1 1 auto;"> <p:dataTable value="#{games.all_games}" editable="true" var="game"> <p:ajax event="rowEdit" listener="#{games.onRowEdit}" /> <p:column headerText="Название"> <p:cellEditor> <f:facet name="output"> <h:outputText value="#{game.name}" /> </f:facet> <f:facet name="input"> <h:inputText value="#{game.name}" /> </f:facet> </p:cellEditor> </p:column> <p:column headerText="Бюджет"> <h:outputText value="#{game.budget}"></h:outputText> </p:column> <p:column headerText="Прибыль"> <h:outputText value="#{game.profit}"></h:outputText> </p:column> <p:column headerText="Оценка"> <h:outputText value="#{game.number}"></h:outputText> </p:column> <p:column headerText="Жанр"> <h:outputText value="#{game.genre}"></h:outputText> </p:column> <p:column headerText="Платформа"> <h:outputText value="#{game.platform}"></h:outputText> </p:column> <p:column headerText="Статус разработки"> <h:outputText value="#{game.status}"></h:outputText> </p:column> <p:column headerText="Начало разработки"> <h:outputText value="#{game.start}"> <f:convertDateTime pattern="dd.MM.yyyy " /> </h:outputText> </p:column> <p:column headerText="Дата релиза"> <h:outputText value="#{game.end}"> <f:convertDateTime pattern="dd.MM.yyyy " /> </h:outputText> </p:column> <p:column style="width:35px"> <p:rowEditor /> </p:column> </p:dataTable> </div> </div> </h:body> </html>
事件未触发的核心原因与修复方案
- 实体类错误注册为Spring单例Bean:
Game.java标注了@Component注解,Spring默认Bean为单例作用域,会导致DataTable遍历时所有行绑定到同一个共享对象,编辑状态下的EL绑定、序列化逻辑异常,直接阻断RowEdit事件流程。修复方式:移除Game类上的@Component注解,将Game作为普通POJO在业务Bean中手动实例化即可。 - 业务逻辑触发未捕获的索引越界异常:
onRowEdit方法中通过All_games.get(new_game.getId())取列表元素,测试数据id为1,但List索引从0开始,仅存1条数据时会直接抛出IndexOutOfBoundsException,异常被JSF异常拦截器吞掉后,前端无任何执行反馈,表现为事件未被调用。修复方式:直接使用event.getObject()返回的编辑后对象同步列表数据,不需要手动按id匹配索引。 - 可编辑列配置不完整:当前表格仅“Название”列配置了
p:cellEditor的输出/输入facet,其余列无编辑能力。PrimeFaces可编辑表格的行编辑校验流程要求,所有参与编辑的列必须完成facet配置,否则点击确认按钮时会因校验不通过直接终止事件传播。修复方式:对需要编辑的列补充p:cellEditor及对应facet配置。
内容的提问来源于stack exchange,提问作者user376237
相关产品推荐
相关产品推荐

