
本文详解 Spring MVC 项目中 JSP 页面能渲染表头但无法显示 List 数据的根本原因,指出 ModelAndView.addObject() 调用正确但常被忽略的 EL 表达式作用域与属性绑定机制问题,并提供可立即生效的修复方案及最佳实践。
本文详解 spring mvc 项目中 jsp 页面能渲染表头但无法显示 `list
在 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())中。而这一行为依赖于两个前提:
- ModelAndView 的视图名(如 "index")必须被 InternalResourceViewResolver 正确解析为 .jsp 文件;
- 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 或空字符串:
-
JSTL 标签库声明完整且无拼写错误(您已正确配置):
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
-
的 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" %>
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; } }✅ 完整修复后的 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 的隐式作用域陷阱。
遵循以上步骤,您的表格数据将立即正常渲染——问题不在数据源,而在模型到视图的“最后一公里”绑定链路。










