Spring MVC 中 JSP 页面无法显示表格数据的解决方案

Spring MVC 中 JSP 页面无法显示表格数据的解决方案

本文详解 Spring MVC 项目中 JSP 页面能渲染表头但无法显示 List 数据的根本原因,指出 ModelAndView.addObject() 调用正确但常被忽略的 EL 表达式作用域与属性绑定机制问题,并提供可立即生效的修复方案及最佳实践。

本文详解 spring mvc 项目中 jsp 页面能渲染表头但无法显示 `list` 数据的根本原因,指出 `modelandview.addobject()` 调用正确但常被忽略的 el 表达式作用域与属性绑定机制问题,并提供可立即生效的修复方案及最佳实践。

在 Spring MVC 应用中,控制器向 JSP 视图传递数据看似简单,实则隐含关键细节。您已确认 GamerDAO.list() 正确返回非空列表(通过 JUnit 验证),JSP 中表头正常渲染、EL 表达式 ${listGamer} 却输出为空——这并非数据库或 DAO 层问题,而是 模型属性未被正确暴露至 JSP 的 EL 作用域 所致。

? 根本原因:addObject() ≠ addAttribute()?不,它们等价,但需确保视图解析正确

首先澄清一个常见误解:ModelAndView.addObject(“listGamer”, listGamer) 在功能上完全等价于 model.addAttribute(“listGamer”, listGamer)。Spring 的 ModelAndView 内部正是将 addObject 委托给 ModelMap.addAttribute 实现的。因此,您的控制器代码本身没有语法错误

真正的问题在于:JSP 页面能否在 EL 表达式中访问到该属性,取决于 Spring MVC 的视图解析器是否将 Model 正确合并到 HttpServletRequest 属性(即 request.setAttribute())中。而这一行为依赖于两个前提:

  1. ModelAndView 的视图名(如 “index”)必须被 InternalResourceViewResolver 正确解析为 .jsp 文件;
  2. InternalResourceViewResolver 必须配置了 exposeSpringMacroHelpers = true(默认为 true),且未禁用模型暴露。

验证控制器逻辑(推荐写法)
虽然 addObject() 合法,但为提升可读性与一致性,建议统一使用 addAttribute():

@RequestMapping(value = "/") public ModelAndView listGamer() {     List<Gamer> listGamer = gamerDAO.list();     ModelAndView model = new ModelAndView("index");     model.addAttribute("listGamer", listGamer); // ✅ 推荐写法,语义清晰     return model; }

? 提示:也可改用更简洁的 @ModelAttribute 或直接返回 String + Model 参数(Spring 4.3+ 推荐):

@RequestMapping(value = "/") public String listGamer(Model model) {     model.addAttribute("listGamer", gamerDAO.list());     return "index"; // 自动解析为 /WEB-INF/views/index.jsp }

?️ JSP 端关键检查项

确保以下三点全部满足,否则 ${listGamer} 将始终为 null 或空字符串:

  1. JSTL 标签库声明完整且无拼写错误(您已正确配置):

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  2. 的 items 属性值必须是非空 Collection
    若 listGamer 为 null,JSTL 会静默跳过循环;若为空 List,则不渲染任何

    。可在调试时添加安全输出:

    <p>Debug: listGamer size = ${empty listGamer ? 'NULL or empty' : fn:length(listGamer)}</p>

    ⚠️ 注意:需额外引入 fn 函数库:

    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
  3. Gamer 类的 getter 方法必须符合 JavaBean 规范
    JSP EL 通过反射调用 getXXX() 方法访问属性。请确认 Gamer 类包含如下标准 getter:

    public class Gamer {     private String name;     private String email;     private String gamertag;     private String phone;     // ... 构造函数、其他字段      public String getName() { return name; }      // ✅ 必须是 getName(), not getname()     public String getEmail() { return email; }     public String getGamertag() { return gamertag; }     public String getPhone() { return phone; } }
  4. ✅ 完整修复后的 JSP 片段(含健壮性增强)

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>  <!DOCTYPE html> <html> <head>     <meta charset="UTF-8">     <title>Game Manager Home</title> </head> <body>     <div align="center">         <h1>Gamer List</h1>          <!-- 调试信息 -->         <p>Model attribute 'listGamer': ${empty listGamer ? 'NOT FOUND or EMPTY' : 'FOUND'}</p>         <p>Size: ${fn:length(listGamer)}</p>          <table border="1" cellpadding="5" cellspacing="0">             <thead>                 <tr>                     <th>#</th>                     <th>Name</th>                     <th>Email</th>                     <th>GamerTag</th>                     <th>Phone</th>                     <th>Action</th>                 </tr>             </thead>             <tbody>                 <c:choose>                     <c:when test="${not empty listGamer}">                         <c:forEach items="${listGamer}" var="gamer" varStatus="status">                             <tr>                                 <td>${status.count}</td> <!-- 从 1 开始计数 -->                                 <td>${gamer.name}</td>                                 <td>${gamer.email}</td>                                 <td>${gamer.gamertag}</td>                                 <td>${gamer.phone}</td>                                 <td><a href="edit?id=${gamer.id}">Edit</a></td>                             </tr>                         </c:forEach>                     </c:when>                     <c:otherwise>                         <tr>                             <td colspan="6" style="text-align:center;">No gamers found.</td>                         </tr>                     </c:otherwise>                 </c:choose>             </tbody>         </table>     </div> </body> </html>

    ? 总结与最佳实践

    • 核心原则:Spring MVC 的 Model 最终会以 request.setAttribute() 方式注入 JSP,EL 表达式 ${xxx} 默认在 page → request → session → application 顺序中查找,因此确保属性存在于 request 作用域是前提。
    • 必查配置:确认 spring-mvc.xml 或 Java Config 中 InternalResourceViewResolver 已注册,且 prefix=”/WEB-INF/views/”、suffix=”.jsp” 匹配实际路径。
    • 开发习惯:在 JSP 中添加 … 或 ${fn:length(listGamer)} 进行空值防御,避免静默失败。
    • 替代方案:如长期维护复杂视图,建议迁移到 Thymeleaf(原生支持 Spring 集成与强类型校验),规避 JSP/EL 的隐式作用域陷阱。

    遵循以上步骤,您的表格数据将立即正常渲染——问题不在数据源,而在模型到视图的“最后一公里”绑定链路。