JSP 调试

JSP 报错在哪看、翻译后的源码、断点与常见错误对照。

Intermediate
Key PointsJSP 的编译错误只写日志不显示在浏览器上,页面空白时第一件事就是看 catalina.out。

页面空白怎么办

  1. 看 logs/catalina.out 或 logs/localhost.<日期>.log
  2. 找 Unable to compile class for JSP 附近的行号
  3. 去 work/Catalina/localhost/<app>/org/apache/jsp/ 看翻译出来的 .java
  4. 对照 .java 的行号定位到 JSP 的原始行
Text
work/Catalina/localhost/myapp/org/apache/jsp/
├── index_jsp.java          ← 你的 index.jsp 翻译结果
├── index_jsp.class
└── user/
    └── list_jsp.java       ← user/list.jsp

The translated .java is the code that actually runs. When wondering "why is this null" or "why is the order wrong", reading it beats adding more printlns.

常见错误对照

MessageCause
Unable to compile class for JSPJava 语法错、缺 import、括号不配对
cannot find symbol: exception页面没标 isErrorPage="true"
The absolute uri ... cannot be resolvedTLD 不在 WEB-INF 或 URI 写错
getOutputStream() has already been called同时用了 out 和 getOutputStream
Response has already been committed输出后才 sendRedirect / sendError
NullPointerException<%= %> 里对 null 调方法

在页面上打印调试信息

JSP
<%-- 开发期临时打开:把 request 里的属性全列出来 --%>
<%@ page import="java.util.*" %>
<%
    Enumeration<String> names = request.getAttributeNames();
    while (names.hasMoreElements()) {
        String n = names.nextElement();
        out.println("<!-- attr: " + n + " = " + request.getAttribute(n) + " -->");
    }
%>

Wrapping in HTML comments keeps the page unchanged while remaining visible in page source. Remove it before release — the request may hold sensitive data.

断点调试

  • JSP 里下断点 —— IDE 会映射到 _jspService 的对应行
  • Java 类里下断点 —— 需要以 debug 模式启动容器
  • 改 JSP 后刷新即可 —— 容器检测到文件时间戳变化会重新翻译
If a JSP change does not take effect, delete the matching directory under work/ and reload. Timestamp resolution (especially on network filesystems) can hide the change and the old compiled class keeps serving — the classic "but I did change it".

日志

JSP
<%
    application.log("用户 " + session.getAttribute("uid") + " 访问了订单页");
    try {
        // 业务
    } catch (Exception e) {
        application.log("订单查询失败", e);      // 带堆栈
        response.sendError(500);
        return;
    }
%>

application.log writes to the container's app log with a timestamp and source. System.out.println only lands in catalina.out with no level and becomes useless at volume.

Friends & Partners