news 2026/9/30 13:45:46

Java Web核心知识全解析:从Servlet到JSP的复习指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web核心知识全解析:从Servlet到JSP的复习指南

1. 复习前先建立全局观:一条 HTTP 请求在 Java Web 应用里到底经历了什么

每次带学生复习 Java Web,我都会先逼问一个问题:你从浏览器地址栏输入一个 URL 并按下回车,到页面刷新出结果,中间到底发生了什么?能答清楚这道题的人,期末试卷上的分数通常不会差。原因很简单,Java Web 这门课的各个知识点——Servlet、JSP、Session、Filter、JDBC——本质上都是在回答这个问题的某一个环节,你只背局部概念不串整体链路,考场上稍微一变题型就懵。

在 Java Web 语境下,最标准的运行环境是 Tomcat + Servlet + JSP + JDBC。整个流程可以拆成几条主线。

首先是请求进入容器。浏览器发的 HTTP 请求到达 Tomcat,Tomcat 的 Coyote 连接器负责接收网络数据,把它解析成HttpServletRequest对象,同时为这次响应准备好HttpServletResponse对象。也就是说,你在 Servlet 里随手用的那个 request,不是你自己 new 出来的,而是容器在收到请求那一刻帮你组装好的,里面塞了请求行、请求头、请求体、参数列表、Cookie 等等一堆东西。

然后是容器找"谁来处理"。Tomcat 根据 URL 的路径去匹配已注册的 Servlet 映射。匹配规则会在后面的章节细讲,期末必考。找到对应 Servlet 后,容器会调用它的service()方法,把 request 和 response 传进去。如果这个 Servlet 之前还没被实例化,容器会先走一遍完整的生命周期初始化流程。

接着是业务处理。Servlet 里写的是控制逻辑:接收参数、调用 JavaBean 或 DAO 层的方法访问数据库、把结果放到 request 或 session 的 attribute 里、最后决定是转发到某个 JSP 还是直接重定向。大部分期末项目在这个环节最容易出问题,因为请求转发和重定向的区别没搞清楚的人,写出来的代码经常出现"页面能跑但逻辑不对"的怪现象。

最后是响应返还。如果用 JSP 做视图层,Tomcat 会把 JSP 翻译成 Servlet、编译、执行,把生成的 HTML 字符串写进 response 输出流,再原路返回给浏览器。这个"JSP 本质也是 Servlet"的认知特别重要,后面我会专门讲怎么在 Tomcat 的 work 目录里查 JSP 编译后的 Java 类,这是很多学校期末笔试和机试都喜欢挖的点。

除了这条主链路,还有两条横切链路也需要纳入全局图:会话管理和过滤。会话管理解决"服务器怎么记住你是谁"的问题,过滤解决"请求在进入 Servlet 之前要经过哪些公共处理"的问题。这两条线都是依托 Servlet 规范里的接口实现的,理解了主链路,它们的位置就非常清楚了。

我建议你复习时自己拿一张白纸,把这条链路从浏览器画到数据库,再画回来,然后在每个节点边上标注对应的核心类和接口。画完这张图,后面背任何知识点都有了挂靠的骨架。期末复习最忌讳上来就背 Servlet 生命周期有哪几个方法,你得先知道生命周期是挂在链路的哪个位置、为什么必须有这几步。

2. Tomcat 目录结构和 Web 应用标准目录结构:这是笔试送分题也是机试丢分重灾区

Tomcat 相关的知识点在期末试卷里一般不难,但覆盖面很广,而且机试时一旦目录结构摆错,整个项目直接 404 或者 500,属于"平时觉得简单、考场上哭都来不及"的典型。我见过太多学生把lib目录建错位置、把 JSP 页面直接丢进WEB-INF里导致无法访问,所以这部分必须踏踏实实过一遍。

2.1 Tomcat 安装根目录里每个文件夹是干什么的

Tomcat 解压出来后那一堆文件夹,期末考填空题的概率不低。bin目录存放启动和关闭脚本,Unix 系机器上跑startup.sh,Windows 上用startup.bat;conf目录是配置文件的大本营,最重要的是server.xml,端口号、虚拟主机、连接器配置都在里面;lib目录放 Tomcat 自身和所有 Web 应用共享的 jar 包;logs目录存运行日志,机试排查 500 错误时第一个该翻的地方;work目录是 JSP 翻译成 Java 源文件和编译后 class 文件的输出位置,后面讲 JSP 编译产物时要到这里来找;webapps目录是部署 Web 应用的默认根目录。

这里有一个特别常考的细节:server.xml默认配置了8080端口,连接器标签是<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />。如果端口被占用,改端口就改这个标签。有些老师喜欢在机试环境里提前把端口改掉,你复习时要养成"启动失败先看日志、再确认端口"的习惯。

2.2 Web 应用目录结构:每个文件夹的位置都藏着考点

一个标准的 Java Web 应用从顶层看是这个样子:

项目根目录(应用上下文根) ├── 静态资源区 │ ├── index.html / index.jsp │ ├── css/ │ ├── js/ │ └── images/ └── WEB-INF(受保护目录) ├── web.xml(部署描述符) ├── classes/(编译后的字节码,含包结构) └── lib/(本应用依赖的第三方 jar)

WEB-INF是整张图里含金量最高的目录。它有几个必须刻在脑子里的特性。

第一,WEB-INF下的资源不能通过浏览器 URL 直接访问。你输入http://localhost:8080/你的应用/WEB-INF/web.xml,服务器会返回 404。这个设计是为了保护部署描述符、类文件和安全配置不被外部直接下载。但注意,服务端代码可以转发到WEB-INF内部的 JSP 页面。很多教科书上的经典写法就是 Servlet 里request.getRequestDispatcher("/WEB-INF/views/index.jsp").forward(...),这个 JSP 在WEB-INF里面,外部无法直达,只能通过 Servlet 转发进入。这个模式既能保护视图,也变相强迫你走 MVC 的控制层,期末项目里用了这个结构通常是加分项。

第二,classes目录下必须保持完整的包结构。比如你写了一个com.example.controller.LoginServlet,编译后的LoginServlet.class必须放在classes/com/example/controller/下。如果手工放置时丢了包路径,Tomcat 启动时会报ClassNotFoundException或NoClassDefFoundError。IDEA 和 Eclipse 的构建工具通常会自动处理,但如果你在机试时被迫手打javac -d参数,就必须自己保证目录层级正确。

第三,lib目录放的是本应用独有的 jar 包。如果你用 Maven 管理依赖,那打包后的WEB-INF/lib会自动生成,不需要手动管理;但传统手工部署时,把 MySQL 驱动 jar 放错地方(比如扔到 Tomcat 的lib而不是应用的WEB-INF/lib)虽然有时也能跑,却会造成不同应用间的类加载污染,后患无穷。

2.3 web.xml 到底在配置什么

在新版本 Servlet 规范里,web.xml不是必须存在的了(Servlet 3.0 以后可以用注解替代),但期末考核它依然非常勤快。web.xml的核心职责包括:注册 Servlet 并配置 URL 映射(对应@WebServlet)、配置 Filter 过滤器链(对应@WebFilter)、配置监听器(对应@WebListener)、配置欢迎页列表、配置 session 超时时间、配置错误页。

web.xml文件的根元素是<web-app>,它带有xmlns和schemaLocation属性,版本号决定了容器用哪一套规则去解析。Java Web 课程里常用的版本是 3.1、4.0。如果你拿了一个 5.0 的 Tomcat,但web.xml头声明的是 3.1,通常也能兼容,但某些新特性不生效,机试时不要在这个地方浪费太多时间。

一个经典的注册 Servlet 配置长这样:

<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.example.controller.LoginServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>

注意里面那个<load-on-startup>,整数越小优先级越高,值为 0 或正整数表示容器启动时就实例化并调用init(),负数或省略则等于延迟到第一次请求时才实例化。期末问"如何让 Servlet 在 Tomcat 启动时自动加载",答案就是这个标签。

3. Servlet 生命周期与请求路径映射:背会十遍不如拆懂一次

Servlet 是 Java Web 的绝对核心。期末卷面上,无论是选择题、简答题还是程序设计题,最终都会落到 Servlet 的几个关键机制上。这一章,我按"生命周期—路径映射—参数传递—请求转发与重定向"四个维度给你拆透。

3.1 生命周期五步曲,每一步容器做了什么事

Servlet 生命周期包含加载与实例化、初始化、服务、销毁、卸载五个阶段,但考试最常问的是三个方法:init()、service()、destroy()。

容器第一次收到匹配某个 Servlet 的请求时,会检查这个 Servlet 是否已经存在。如果不存在,就用Class.forName()加载类,通过无参构造器创建实例,然后调用init(ServletConfig config)。init方法只执行一次,适合做初始化操作,比如读取配置文件、初始化数据库连接池。init方法里可以拿到ServletConfig,通过getInitParameter()读取web.xml里配置的初始化参数,这是一个高频考点。

实例创建之后,每个请求到来时容器都会调用service()方法。HttpServlet 默认已经把service()实现成了按 HTTP 方法分发的逻辑:GET 请求丢给doGet(),POST 请求丢给doPost(),另外还有 doPut、doDelete 等。所以你在写自定义 Servlet 时通常只重写doGet()和doPost(),不用碰service()。

销毁阶段发生在容器关闭或应用停止时。destroy()方法释放资源,比如关闭数据库连接、停止后台线程。这里有个很容易忽视的点:Servlet 是单实例多线程的。也就是说,Tomcat 只会创建 Servlet 的一个实例,所有并发请求都共享这个实例。这意味着实例变量不是线程安全的。你如果在 Servlet 里写了一个private int count;然后多线程环境下做自增,结果一定错乱。正确的做法是把需要共享的数据放进request、session或application作用域,或者干脆用局部变量。

3.2 请求路径匹配规则:不加斜杠的坑,调了半小时才发现

Servlet 的 URL 映射规则是期末高频考点,也是机试常见的隐形陷阱。一个映射配置写在注解里:

@WebServlet("/login") public class LoginServlet extends HttpServlet { ... }

也可以写在web.xml里。容器匹配 URL 时,按以下顺序判断优先级:

  1. 精准匹配:/login、/user/info,完全相等才命中。
  2. 目录匹配:/user/*,前缀匹配。
  3. 扩展名匹配:*.do、*.action。
  4. 默认匹配:/,匹配所有未处理的请求。

优先级从高到低,精准匹配永远排第一。考试常以选择题形式问你:给定一堆映射和若干 URL,判断哪个 Servlet 会被调用。你只要记住"精准 > 目录 > 扩展名 > 默认"这个顺序就不会错。

机试里有个特别坑的操作:映射路径忘记加前导斜杠,写成@WebServlet("login")。启动时 Tomcat 会直接报错,提示映射路径必须以/或*.开头。这种错误说白了就是编码细节,但每年都有学生踩。所以复习时建议你亲手把几个典型的映射配一遍,再手动访问几个 URL 验证命中的是谁。

3.3 三大作用域对象:东西该放哪,不只是 API 记忆题

Java Web 中的属性传递通常依托三个作用域对象:HttpServletRequest、HttpSession、ServletContext。期末经常让你辨析它们的存活范围。

  • request作用域:存活时间为一次请求,如果服务器内部做了转发(forward),则转发前后的 Servlet 和 JSP 共享同一个 request 对象。适合放本次请求产生的临时数据,比如表单校验错误、查询结果列表。
  • session作用域:存活时间为一个会话,从用户首次访问到会话超时或手动失效。适合放登录状态、购物车、用户偏好。
  • application作用域(即ServletContext):存活时间为整个应用从启动到关闭。适合放全局配置、缓存、统计计数器。因为所有用户共享同一个ServletContext,所以要注意并发写问题。

一道经典的设计题是:用户登录后,如何在多个页面显示"当前用户名"?标准答案是登录成功后session.setAttribute("user", user),然后在 JSP 里用${sessionScope.user.username}取出。如果把用户对象放 request,转发到下一个页面还能取到,但浏览器刷新一次就没了;放 application 则所有用户串数据,彻底错误。这些判断思路比死记 API 有用得多。

3.4 转发与重定向:浏览器地址栏会出卖你的理解

请求转发(forward)和重定向(sendRedirect)的对比题,几乎每个期末试卷都有。

转发是服务器内部行为。浏览器只发一次请求,服务器在内部把请求转交给另一个资源(Servlet 或 JSP),整个过程浏览器无感知,地址栏不变。代码写法:

request.getRequestDispatcher("/success.jsp").forward(request, response);

转发可以携带原始的 request 和 response,所以转发前塞进 request 作用域的数据,转发后的页面依然能取到。

重定向是服务器给浏览器返回一个 302 响应,响应头Location字段指向新地址,浏览器再发起第二次请求。地址栏会变成新地址。代码写法:

response.sendRedirect("/admin/login");

重定向不能直接携带 request 里的属性,因为第二次请求是一个全新请求,想带数据得用 session 或者 URL 参数。

考试判断题最爱这么出:"请求转发时浏览器地址栏不变,重定向时地址栏变化,且重定向会丢失 request 属性。"这句话是对的,但要注意重定向的目标 URL 如果是本项目内资源,要写应用上下文路径。很多机试代码写成response.sendRedirect("/success.jsp"),结果发现自己部署的应用上下文名是myapp,实际跳转地址应该是/myapp/success.jsp,不写上下文名就会跳到一个不存在的根目录地址。正确写法是:

response.sendRedirect(request.getContextPath() + "/success.jsp");

4. JSP 的本质与"查编译后的 Java 类"实操:把这层窗户纸捅破

很多学生学 JSP 的时候很苦恼:它是 HTML 又是 Java,到底是前台还是后台?其实 JSP 本质上就是一个"以写 HTML 为主、内嵌 Java 代码片段"的 Servlet。Tomcat 在第一次访问 JSP 时,会经历翻译、编译、加载、执行四个阶段。搞懂这个机制,JSP 的指令、脚本元素、内置对象就全部串起来了。

4.1 JSP 从源码到 HTML 的完整编译链

你在浏览器访问index.jsp,Tomcat 内部做的事是:

  1. 找到index.jsp文件。
  2. 检查work目录下有没有对应的翻译产物,如果 JSP 文件改动过或产物不存在,就把它翻译成一个 Java 源文件,类名一般是index_jsp.java。
  3. 用 javac 将 Java 源文件编译成index_jsp.class。
  4. 加载这个类,创建实例,调用它的_jspService()方法(这个方法对应 Servlet 的service())。
  5. 把 JSP 里的标签和 Java 代码生成 HTML 内容,写进 response 输出流。

也就是说,JSP 本质上是一个继承自HttpJspBase的 Servlet。JSP 页面里的模板文本(那些不带特殊标记的 HTML),会被翻译成out.write("...")这样的输出语句;<% ... %>里的 Java 代码会原样嵌入_jspService()方法体;<%= 表达式 %>会被翻译成out.print(表达式)。

知道这条编译链后,"查看 JSP 编译后的 Java 类"这个机试操作就很自然了。具体路径是:Tomcat安装目录/work/Catalina/localhost/你的应用名/org/apache/jsp/index_jsp.java。不同 Tomcat 版本的目录结构略有差异,但大方向都是work目录下按Catalina/host/应用名层级存放。考试时如果老师问"JSP 编译生成的 Java 类文件存放位置",答案就是work目录。

为什么要强调这个?因为期末考试经常出一类题:JSP 里的某个变量为什么在另一个脚本片段里访问不到?打开index_jsp.java一看就明白——所有<% %>里的代码都是拼在同一个_jspService()方法里的局部变量,一个片段里声明了int x = 1;,只要后面片段还在同一个方法里,就能继续访问;但如果你想在<%! %>声明实例方法或全局成员变量,那就属于类的层面,作用范围是整页。理解了编译产物,你不需要背这些规则都能推理出来。

4.2 JSP 三大指令和六大内置对象

JSP 指令的作用是"告诉容器在翻译阶段如何处理本页面"。最常用的是三个:

  • page指令:设置页面属性,比如<%@ page contentType="text/html;charset=UTF-8" %>、<%@ page import="java.util.*" %>、<%@ page isErrorPage="true" %>。期末选择题喜欢考pageEncoding和contentType的区别,前者是 JSP 文件本身的编码,后者是响应内容的编码和类型。
  • include指令:静态包含,<%@ include file="header.jsp" %>,在翻译阶段直接把被包含文件的内容合并进来,最终只生成一个 Java 类。
  • taglib指令:引入标签库,<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,引入 JSTL 后才能在页面里用<c:forEach>。

JSP 九大内置对象里,期末最常考的是这四个:out(类型是JspWriter,负责输出)、request(HttpServletRequest)、session(HttpSession)、application(ServletContext)。对,session和application的底层其实就是上一章讲的两个作用域对象,只是 JSP 里角色变了。

还有一个常被忽略的pageContext。它本身也是内置对象(类型PageContext),同时它还可以作为门面来访问其它作用域:pageContext.getRequest()、pageContext.getSession()、pageContext.getServletContext()。更重要的能力是findAttribute(String name),它会依次从 page、request、session、application 四个作用域里找同名属性,找到就返回。JSP 表达式语言${user.name}的取值逻辑就依赖这一套机制。

4.3 EL 和 JSTL:不写 Java 脚本片的正确姿势

<% ... %>写起来爽,但页面一旦复杂就变成一堆代码交织的"意大利面",维护难度直线上升。所以课程后期都会引入 EL 表达式语言和 JSTL 标签库。

EL 的语法是${表达式}。它支持.和[]两种属性访问方式:${user.name}等价于${user["name"]}。EL 运算符里有一个容易被忽视的点:${empty list}判断集合是否为 null 或长度为 0,经常用来判断用户是否已登录、列表是否有数据。

JSTL 的核心标签是c:if、c:forEach、c:choose。一个典型的列表渲染长这样:

<c:forEach items="${userList}" var="u" varStatus="status"> <tr> <td>${status.index + 1}</td> <td>${u.username}</td> <td>${u.role}</td> </tr> </c:forEach>

items指向集合对象,var是循环变量名,varStatus可以拿到循环的索引。注意status.index从 0 开始,显示序号时通常+1。期末编程题如果要求"表格列出所有学生名单",用这段代码就是标准答案。

关于 JSP 还有一个高频判断题:<%@ include %>和<jsp:include>的区别。前者是静态包含,在翻译阶段合并,最终编译成一个类,适合引入固定不变的头部尾部;后者是动态包含,运行时才发起请求,被包含资源有自己的请求处理过程,每次访问都要额外开销。两者使用场景完全不同。

5. 会话跟踪:Cookie 与 Session 的底层运作方式和期末考试陷阱

"HTTP 是无状态协议"这句话,Java Web 课程里反复讲,但到底意味着什么?就是浏览器每次请求相互独立,服务器默认不记得你上一次请求做过什么。会话跟踪技术就是为了在无状态的 HTTP 上维持"有状态"的体验而出现的。期末这部分的核心考点就是 Cookie 和 Session,以及它们怎么配合。

5.1 Cookie:放在浏览器端的钥匙串

Cookie 是服务器通过Set-Cookie响应头下发给浏览器的一段小数据,浏览器保存下来,下次请求时通过Cookie请求头原样带回。这个机制决定了 Cookie 本身是存在客户端的。

在 Servlet 里写一个 Cookie 非常简单:

Cookie c = new Cookie("username", "zhangsan"); c.setMaxAge(60 * 60 * 24); // 有效期,单位秒,一天 c.setPath(request.getContextPath() + "/"); // 作用路径 response.addCookie(c);

读回 Cookie:

Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie c : cookies) { if ("username".equals(c.getName())) { // c.getValue(); } } }

期末考试爱考setMaxAge的几个值的含义:不设置(默认 -1)表示浏览器关闭就失效;设置为 0 表示立即删除;正整数表示存活多少秒。机试里"自动登录""记住我"功能就是靠一个存了用户标记、有效期很长的 Cookie 实现的,不过真正生产环境不可能存明文用户名,通常存一个 token 并关联到服务端,这里不展开。

Cookie 的安全限制也要记清楚:Cookie 大小受限(一般 4KB 左右),且浏览器对单个域名下的 Cookie 数量有限制。所以大头数据不能靠 Cookie 传。

5.2 Session:把数据存在服务器端的"储物柜"

Session 弥补了 Cookie 容量小和不安全的缺点。用户第一次访问时,服务器创建一个HttpSession对象,生成一个唯一的 Session ID,通过 Cookie 把JSESSIONID=xxx下发给浏览器;后续每次请求,浏览器带着JSESSIONID回来,服务器凭这个 ID 找到之前创建的 Session。

所以 Session 机制的前提是:服务器靠 Cookie 维护这个 Session ID。如果用户禁用了 Cookie,JSESSIONID 传不回来,服务器就找不到 Session 了。解决方案是 URL 重写,也就是在 URL 后面拼上;jsessionid=xxx。Servlet 规范里用response.encodeURL()这个 API 来统一处理,容器会自动判断当前是否带 Cookie,如果没带就自动往 URL 上追加。期末判断题"禁用 Cookie 后 Session 就无法使用了",答案是错的——如果程序员调用了 URL 重写,Session 依然可用,只是体验降级。

HttpSession的关键方法有getId()、setAttribute()、getAttribute()、removeAttribute()、invalidate()、getMaxInactiveInterval()和setMaxInactiveInterval()。invalidate()是立刻让 Session 失效,登出功能的标配代码。getMaxInactiveInterval()返回的是会话最大空闲时间,单位秒,默认 30 分钟。一旦超过这个时间用户没有发起任何请求,服务器就自动销毁 Session。

5.3 高频案例:登录状态的保存与退出

把前面这些串成一个期末考试必练的模块:登录与注销。

登录逻辑的标准流程是:

  1. 接收用户名和密码,调 DAO 查数据库验证。
  2. 验证失败:request.setAttribute("msg", "用户名或密码错误"),转发回登录页(forward,所以 msg 能显示出来)。
  3. 验证成功:session.setAttribute("user", user),然后response.sendRedirect("index")。这里为什么用重定向?因为如果直接转发,用户刷新页面时浏览器会重新执行一次 POST 提交,导致表单重复提交——这是一个很容易茬的地方。重定向是全新 GET 请求,刷新不会重复提交。

退出逻辑的标准流程是:

  1. 拿到当前 Session:request.getSession()(如果没有就返回新 Session,注意和getSession(false)区别,后者没有就返回 null)。
  2. 清理用户数据并让 Session 失效:session.invalidate()。
  3. 重定向到登录页。

这个模块几乎每年都出现在期末程序题或机试里,建议你在考试前亲手完整敲一遍,尤其注意登录成功后千万别在重定向后还往 request 里塞数据——重定向后的请求拿不到。

6. 过滤器、监听器与 MVC 分层:项目代码的骨骼和门卫

期末试卷后半段通常会出现项目综合题:给你一个需求,让你设计目录结构、画请求流程图、写关键代码。这种题考察的不是单个 API,而是你有没有"分层设计"的意识。这一章把过滤器、监听器和 MVC 三个知识点收在一起讲,因为它们在项目中通常是一套组合拳。

6.1 Filter 的工作原理与常见用途

Filter 的思想是"请求进入 Servlet 之前先过一道门,响应返回浏览器之前再过一道门"。Servlet 规范允许你配置一长串 Filter,形成过滤器链。容器按过滤器的注册顺序依次调用,每个 Filter 调doFilter()方法时,可以通过chain.doFilter(req, resp)决定是否放行到下一个 Filter 或目标 Servlet;如果直接 return 不放行,请求就被拦截了。

一个典型的字符编码过滤器长这样:

@WebFilter("/*") public class EncodingFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=UTF-8"); chain.doFilter(req, resp); } }

这个过滤器解决了全站中文乱码问题,可以说是每个项目的"标配组件"。期末如果要求写一个 Filter,字符编码是最百搭的答案。

Filter 的其它实际用途还包括:登录校验(未登录用户访问受保护页面时拦截并重定向到登录页)、敏感词过滤、日志记录、统一权限控制。机试里最常见的场景是"未登录用户不能访问 list.jsp",这时候用 Filter 会非常优雅:

  1. 放行登录页、登录 Servlet、静态资源。
  2. 其它请求都检查 Session 里有没有 user。
  3. 没有 user 就重定向到登录页,有就chain.doFilter。

Filter 的生命周期和 Servlet 很像,也有init()、doFilter()、destroy()三个方法,但注意 Filter 每次调用的是doFilter而不是service。

6.2 Listener 监听器的几个常考类型

监听器用于监听 Web 应用中某些事件的发生,并在事件触发时执行回调。按监听对象分类,常考几类:

  • ServletContextListener:监听应用启动和关闭。最常用的场景是在应用启动时初始化数据库连接池、加载全局配置。方法有contextInitialized()和contextDestroyed()。
  • HttpSessionListener:监听 Session 的创建和销毁。可以通过sessionCreated()统计在线人数。
  • ServletRequestListener:监听请求的创建和销毁,通常用来记录请求日志。

期末考试问"如何在应用启动时执行一段初始化代码",两个可答题方向:<load-on-startup>指定一个 Servlet,或者写一个ServletContextListener。后者更符合规范姿势,因为监听器不承载业务请求逻辑,专门干这种横切的事情。

6.3 MVC 三层结构的标准模板

JSP + Servlet + JavaBean 的 MVC 分层,是期末项目考察的重心。它的分工是:

  • Model(模型层):JavaBean + DAO,负责业务数据和数据库操作。
  • View(视图层):JSP 页面,只负责展示数据,不写复杂逻辑。
  • Controller(控制层):Servlet,负责接收请求、调用 Model、选择 View。

一个典型的 MVC 请求流程是:浏览器发请求 → Controller 接收 → Controller 调 Model 取数据 → Controller 把数据放进 request → 转发到 View → View 渲染 HTML 返回。

对应到实际项目里,包结构通常长这样:

src/com/example/ ├── controller/ # Servlet 层,如 LoginServlet, UserServlet ├── service/ # 业务逻辑接口和实现,如 UserService, UserServiceImpl ├── dao/ # 数据库访问层,如 UserDao, UserDaoImpl ├── entity/ # 实体类,如 User └── util/ # 工具类,如 DBUtil webapp/ ├── WEB-INF/ │ ├── web.xml │ └── views/ # 受保护视图 ├── css/js/images └── index.jsp

有些教材把 service 层合并进 DAO 层,但期末设计题里出现三层(Controller/Servlet 层、Service/Business 层、DAO/持久层)往往比两层更容易拿分。因为 Service 层能让业务逻辑和数据库操作解耦,体现出你有工程化思维。

数据库连接这一块,期末项目常用 JDBC 直连。标准套路是Class.forName("com.mysql.cj.jdbc.Driver")、DriverManager.getConnection(url, user, password)、PreparedStatement执行 SQL、ResultSet遍历结果。特别注意PreparedStatement能防 SQL 注入,笔试简答题如果问"如何防止 SQL 注入",标准答案就是用预编译语句而不是字符串拼接 SQL。这是个非常容易送分也容易丢分的点。

7. 考前 48 小时复习清单与两道经典程序设计题演练

最后这部分,我不讲新知识,给你一份可以直接照着做的考前检查单,再带你把两道高频程序设计题完整过一遍。这两道题几乎覆盖了前面所有核心知识点,写一遍等于把整门课复习了一大半。

7.1 应试自检清单:每条都能对着课本找到答案

  • 能否画出从浏览器发请求到 Servlet 再到 JSP 返回的完整流程图?
  • 能否默写 Tomcat webapps 目录里一个标准 Web 应用的目录结构,并说明 WEB-INF 有什么特殊含义?
  • 能否说出 Servlet 生命周期三个方法在什么时机被调用、各自执行几次?
  • 能否分清 request、session、application 三个作用域的存活时间,并各举一个适用场景?
  • 能否解释 forward 和 sendRedirect 的区别,并说出各自应用场景?
  • 能否说出 JSP 编译后的 Java 类存放在 Tomcat 哪个目录下?内置对象里out和response.getWriter()有什么区别?
  • 能否解释 Session 依赖 Cookie 的原理,以及 Cookie 被禁用后如何通过 URL 重写继续使用 Session?
  • 能否写一个登录校验 Filter,并说出它在过滤器链中的位置为什么重要?
  • 能否说出 EL 表达式${sessionScope.user != null}和${user != null}的区别?(后者会在所有作用域里找属性,消耗稍高,建议写明确作用域。)
  • 能否手写 JDBC 的完整步骤(加载驱动、获取连接、预编译 SQL、执行、释放资源)?

每一条如果答不上来,回到对应章节把这个知识点的关键词背一遍,然后立刻敲一遍代码验证。复习的优先级是:程序题 > 概念辨析 > 代码填空 > 项目设计。时间不够时先保程序题。

7.2 高频程序题一:实现登录功能(覆盖 Servlet、Session、JDBC)

题目描述:请使用 Servlet + JSP + JDBC 实现一个简单的登录功能。要求登录成功后进入欢迎页显示当前用户名,登录失败则返回登录页并提示错误信息。密码不得使用 SQL 字符串拼接。

核心代码参考:

@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); // 这里应调用 service 或 dao 层,示意直接写 UserDao userDao = new UserDao(); User user = userDao.findByUsernameAndPassword(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("user", user); response.sendRedirect(request.getContextPath() + "/welcome.jsp"); } else { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } }

DAO 层的查询应该用PreparedStatement:

String sql = "SELECT * FROM user WHERE username = ? AND password = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if (rs.next()) { User u = new User(); u.setId(rs.getInt("id")); u.setUsername(rs.getString("username")); return u; }

welcome.jsp里通过${sessionScope.user.username}显示用户名。整个流程把 Servlet、Session、JDBC、EL 全串起来了,机试时照着这个骨架扩展即可。

7.3 高频程序题二:实现简单的用户列表分页查询(覆盖 MVC、JSTL、DAO)

题目描述:实现用户列表的分页展示,每页显示 5 条数据,页面底部显示上一页、下一页,并显示当前页数和总页数。

思路拆解:

  1. 获取当前页码参数:request.getParameter("page"),如果为空或非法,默认第一页。
  2. DAO 提供两个方法:findByPage(page, pageSize)返回List<User>;count()返回总记录数。
  3. Controller 算总页数:totalPage = (total + pageSize - 1) / pageSize。
  4. 把当前页数据、当前页、总页数放进 request,转发到 JSP。
  5. JSP 用<c:forEach>渲染列表,用<a>拼接上一页、下一页链接。

SQL 里分页语句在不同数据库写法不同,MySQL 是LIMIT ?, ?:

String sql = "SELECT * FROM user LIMIT ?, ?"; ps.setInt(1, (page - 1) * pageSize); ps.setInt(2, pageSize);

重点理解offset = (page - 1) * pageSize这个公式,期末填空和程序题都爱考。

7.4 最后一天,把注意力放在这三点上

  • 把项目跑起来。机试不像笔试,代码写错了编译期就能发现,但运行时 404、500、乱码、空指针,考场上全靠冷静排查。建议考试前把你自己的项目完整启动一次,确认 Tomcat 版本、JDK 版本、MySQL 驱动版本没有隐性冲突。
  • 背一遍常见的 500 报错原因:空指针一般是对象没初始化或从 request 里取了一个 null;ClassNotFoundException 一般是 jar 没引入或放错目录;404 一般是 URL 路径映射写错或资源不存在。看到异常先镇定,按这个思路定位。
  • 写代码前先理清包名、类名、映射路径、数据库表结构,保证四者一致。好多学生机试挂在自己命名对不上:Servlet 里访问/list,但注解写的是/listServlet,页面又跳转到list.jsp,一个名字对不上全盘白搭。

Java Web 这门课说到底是实践出真知,考前临时背再多概念,也不如亲手把登录、列表、增删改查、过滤器这四件事各写一遍。写的过程中你会发现,那些背不下来的 API 慢慢都长在肌肉记忆里了。祝考试顺利。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 13:44:19

Switch Case 嵌套完全指南:语法、状态机实战与性能避坑

写了十几年代码&#xff0c;switch case这个结构我几乎天天都在碰&#xff0c;但真正把它用明白、尤其是嵌套写法用得干净不留坑&#xff0c;反倒是带新人那几年才系统梳理清楚的。很多人第一反应是"这玩意儿不就是个多分支判断吗&#xff0c;能有多难"&#xff0c;结…

作者头像 李华
网站建设 2026/9/30 13:42:12

晓多客服机器人AI工作流落地实战指南

简介&#xff1a;本资源是一份聚焦AI客服落地实践的专业技术文档&#xff0c;面向客服系统开发者、智能客服产品运营者及人工智能应用研究者&#xff0c;深入解析晓多客服机器人如何通过深度学习与自然语言理解技术&#xff0c;解决家电、电商等行业售前型号对比、售后并发接待…

作者头像 李华
网站建设 2026/9/30 13:42:06

自动标注闭环实战:从Grounded-SAM到autodistill

开头直接进入正题&#xff0c;不讲废话。我先说清楚这篇文章是什么&#xff1a;这是一份把 X-AnyLabeling、autodistill 和 Grounded-SAM 串起来做自动标注的完整实战记录&#xff0c;覆盖从环境部署到批量出标签再到人工修正的全流程。前前后后折腾了小半个月&#xff0c;踩了…

作者头像 李华
网站建设 2026/9/30 13:41:05

DevPress:借力CSDN生态搭建自主可控开发者社区

做开发者关系或者技术品牌运营的朋友&#xff0c;大概率都遇到过这样一个两难的局面&#xff1a;公司在公域平台上攒了几万粉丝、几百篇技术文章&#xff0c;看起来热闹&#xff0c;可一旦平台规则变动、流量分发策略调整&#xff0c;或者想做个官方活动、上新产品、沉淀一批核…

作者头像 李华
网站建设 2026/9/30 13:40:54

FTP服务系统设计与实现:协议拆解、权限模型与并发测试全解析

简介&#xff1a;一套完整的FTP服务系统设计与实现毕业论文&#xff0c;面向计算机相关专业学生、毕业设计课题研究者以及需要完成网络应用开发的初学者。论文以软件工程方法为主线&#xff0c;从FTP协议基于TCP/IP与客户端/服务器架构的原理入手&#xff0c;完整覆盖课题背景与…

作者头像 李华