JSP 结构

JSP 在 MVC 中的位置、请求流转、转发与包含。

Beginner
Key PointsServlet 收请求、调业务、把结果放进 request 域,再 forward 给 JSP 渲染——这是 JSP 时代的标准结构。

MVC 里的位置

Text
Browser
   ↓ GET /user?id=42
Controller (Servlet)
   ├─ 取参数、校验
   ├─ 调 Service / DAO
   ├─ request.setAttribute("user", user)
   └─ forward → /WEB-INF/views/user.jsp
                     ↓
                  View (JSP)  ← 只读数据,不查库、不算业务
                     ↓ HTML
                  Browser

Controller 的典型写法

Java
@WebServlet("/user")
public class UserServlet extends HttpServlet {

    private final UserService service = new UserService();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        int id;
        try {
            id = Integer.parseInt(req.getParameter("id"));
        } catch (NumberFormatException e) {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "id 不合法");
            return;
        }
        User user = service.find(id);
        if (user == null) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        req.setAttribute("user", user);
        req.getRequestDispatcher("/WEB-INF/views/user.jsp").forward(req, resp);
    }
}

View 只做呈现

JSP
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html><body>
  <h1>${user.name}</h1>
  <p>邮箱:${user.email}</p>
  <c:if test="${empty user.email}">
    <p>未填写邮箱</p>
  </c:if>
</body></html>

Seeing Connection, SELECT or new SimpleDateFormat() in a view means the boundary is broken. Those belong to the controller and service; the view only places data that is already computed.

转发与包含

MechanismSyntaxBehaviour
forward<jsp:forward page="a.jsp"/>交出控制权,当前页后续内容不执行
include (static)<%@ include file="h.jsp" %>编译期合并成一个文件
include (dynamic)<jsp:include page="h.jsp"/>运行期分别执行再拼输出
JSP
<%-- 静态包含:适合页头页脚这种不变的片段,只翻译一次 --%>
<%@ include file="/WEB-INF/parts/header.jsp" %>

<main>正文内容</main>

<%-- 动态包含:适合内容会变的片段,可以传参数 --%>
<jsp:include page="/WEB-INF/parts/sidebar.jsp">
  <jsp:param name="section" value="news"/>
</jsp:include>
Static include merges at compile time, so duplicate variable names or conflicting page attributes break it; dynamic include runs at runtime and stays isolated but is slightly slower. Prefer dynamic when the fragment needs variables.

为什么视图放 WEB-INF

/WEB-INF/views/user.jsp is reachable only by forward; typing the URL gives 404. That guarantees the controller always runs first — otherwise a user could bypass the permission check and hit a page whose data was never prepared.

Friends & Partners