Servlet学习笔记

1.Servlet技术应用

1.1 处理客户端请求

1.1.1 Servlet简介

Servlet(Server Applet)是运行在服务器端的Java程序,属于Java EE核心组件之一,主要用于接收和处理客户端(浏览器)发送的HTTP请求,生成HTTP响应,实现前后端数据交互。

核心特点:

  • 运行在服务器端(如Tomcat、Jetty等Web服务器),由服务器负责实例化和调用。

  • 基于Java语言开发,具备跨平台性,遵循Java EE规范。

  • 采用"请求-响应"模型,专门处理HTTP协议相关的请求与响应。

  • 可与数据库、JavaBean等组件配合,实现复杂的Web业务逻辑。

Servlet功能

  • 处理客户端请求:Servlet可以接收来自客户端(如浏览器)的HTTP请求,并根据请求的内容进行相应的处理。

  • 生成动态内容:Servlet可以生成HTML、XML、JSON等格式的动态内容返回给客户端。

  • 控制和管理会话:Servlet支持会话跟踪,通过维护会话状态为用户提供连续的服务体验。

  • 过滤和拦截请求:通过使用Filter(过滤器),Servlet可以对请求和响应进行预处理和后处理,比如编码转换、权限验证等。

核心接口与类:Servlet接口是所有Servlet的顶层接口,定义了Servlet的核心方法;GenericServlet是Servlet接口的抽象实现类,简化了Servlet的开发;HttpServlet是GenericServlet的子类,专门用于处理HTTP请求,是实际开发中最常用的Servlet类。

2.1.2 了解HTTP协议

HTTP(HyperText Transfer Protocol,超文本传输协议)是客户端与Web服务器之间通信的标准协议,属于无状态、无连接的应用层协议。

核心特点:

  • 无状态:服务器不保存客户端的会话信息,每次请求都是独立的,需通过Cookie、Session等机制实现会话跟踪。

  • 无连接:客户端发送请求后,服务器响应完毕即断开连接(HTTP/1.1支持长连接,可复用连接减少开销)。

  • 基于请求-响应模型:客户端发送请求(Request),服务器返回响应(Response),缺一不可。

HTTP请求(Request):客户端向服务器发送的请求信息,包含请求行、请求头、请求体三部分。

  • 请求行:包含请求方法(GET、POST、PUT、DELETE等)、请求URL、HTTP协议版本,例如:GET /demo?name=test HTTP/1.1。

  • 请求头:携带客户端相关信息(如浏览器类型、Cookie、请求编码等),例如:User-Agent、Cookie、Content-Type。

  • 请求体:仅POST请求有,用于携带大量请求参数(如表单数据),GET请求的参数拼接在URL后。

html 复制代码
POST /web01/welcome HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
Origin: http://localhost:8080
Connection: keep-alive
user=admin

HTTP响应(Response):服务器向客户端返回的响应信息,包含响应行、响应头、响应体三部分。

  • 响应行:包含HTTP协议版本、状态码、状态描述,例如:HTTP/1.1 200 OK。

  • 响应头:携带服务器相关信息(如响应编码、会话Cookie、页面跳转等),例如:Content-Type、Set-Cookie、Location。

  • 响应体:服务器返回的具体内容(如HTML页面、JSON数据、文本等),是客户端最终展示的内容。

html 复制代码
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Date: Wed, 12 Feb 2025 08:06:28 GMT
Keep-Alive: timeout=20
Connection: keep-alive
Content-Length: 94
<html><head><title>
欢迎页面
</title></head><body>
欢迎您:admin
</body></html>

常用HTTP状态码:

HTTP响应状态码是服务器在接收到客户端请求后返回的一个三位数字代码,用于表示请求的处理结果。它分为五个类别,每个类别以不同的百位数字开头。

类别 状态码范围 说明
**信息响应 (1xx)**​ 100 继续
101 切换协议
102 处理中
103 早期提示
**成功响应 (2xx)**​ 200 OK
201 已创建
202 已接受
203 非权威信息
204 无内容
205 重置内容
206 部分内容
**重定向 (3xx)**​ 300 多项选择
301 永久移动
302 临时移动
303 查看其他位置
304 未修改
305 使用代理
307 临时重定向
308 永久重定向
**客户端错误 (4xx)**​ 400 错误请求
401 未认证
402 需要付款
403 禁止访问
404 未找到
405 方法不被允许
406 不接受
407 需要代理认证
408 请求超时
409 冲突
410 丢失
411 长度要求
412 前置条件失败
413 请求体过大
414 URI太长
415 不支持的媒体类型
**服务器内部错误 (5xx)**​ 500 服务器内部错误
501 未实现
502 错误的网关
503 服务不可用
504 网关超时
505 HTTP版本不支持

**常用HTTP状态码:**200(请求成功)、404(资源不存在)、500(服务器内部错误)、302(重定向)、400(请求参数错误)。

2.1.3 使用HttpServlet处理客户端请求

HttpServlet是处理HTTP请求的核心类,继承自GenericServlet,重写了service()方法,根据HTTP请求方法(GET、POST等)分发到对应的doGet()、doPost()等方法中,开发中只需重写对应方法即可。

核心步骤:

  1. 创建Java类,继承HttpServlet。

  2. 重写doGet()、doPost()等方法(根据业务需求选择,GET用于获取资源,POST用于提交资源)。

  3. 通过request对象获取客户端请求参数、请求头等信息。

  4. 通过response对象设置响应头、响应编码,向客户端输出响应内容。

  5. 配置Servlet(通过web.xml或注解),使服务器能够识别并调用该Servlet。

核心对象:

  • HttpServletRequest:封装客户端HTTP请求信息,常用方法:getParameter(String name)(获取请求参数)、getSession()(获取会话对象)、getHeader(String name)(获取请求头)、setCharacterEncoding(String encoding)(设置请求编码)。

  • HttpServletResponse:封装服务器HTTP响应信息,常用方法:getWriter()(获取字符输出流,输出文本/HTML)、getOutputStream()(获取字节输出流,输出文件/图片)、setContentType(String type)(设置响应内容类型)、sendRedirect(String url)(重定向)。

注意事项:处理POST请求时,需在doPost()方法开头设置请求编码(request.setCharacterEncoding("UTF-8")),避免中文乱码;响应时需设置响应编码(response.setContentType("text/html;charset=UTF-8"))。

HttpServletRequest的常用方法

方法所属对象 方法名 描述
HttpServletRequest getHeader(String name) 返回参数指定的HTTP请求的Header数据
HttpServletRequest getRequestURI() 返回HTTP请求的URI
HttpServletRequest getMethod() 返回HTTP请求方法
HttpServletRequest getAttribute(String name) 根据参数给定的属性名返回属性值
HttpServletRequest setAttribute(String name, Object obj) 在ServletRequest中设置属性
HttpServletRequest getInputStream() 返回以二进制数方式直接读取客户请求数据的输入流
HttpServletRequest getParameter(String name) 根据给定的参数名返回参数值
HttpServletRequest getRemoteAddr() 返回远程客户端主机的IP地址
HttpServletRequest setCharacterEncoding(String s) 设置解析请求参数时使用的字符编码类型
HttpServletResponse setHeader(String name, String value) 设置HTTP响应的Header,如果参数name对应的Header已经存在,则覆盖原来的数据
HttpServletResponse addHeader(String name, String value) 向HTTP响应中加入Header
HttpServletResponse getOutputStream() 返回可以向客户端发送二进制数据的输入流对象ServletOutputStream
HttpServletResponse getWriter() 返回可以向客户端发送字符数据的PrintWriter对象
HttpServletResponse setContentType(String type) 设置Servlet发送的响应数据的MIME类型

2.2 Servlet生命周期和配置

2.2.1 Servlet生命周期

Servlet的生命周期是指Servlet从创建到销毁的整个过程,由Web服务器(如Tomcat)全程管理,分为4个阶段,核心方法对应生命周期的不同阶段。

生命周期四阶段:

  1. 加载与实例化:服务器启动时(或第一次访问Servlet时),加载Servlet类,通过反射创建Servlet实例(默认单例模式,一个Servlet类只有一个实例)。

  2. 初始化:实例创建后,立即调用init(ServletConfig config)方法,用于初始化Servlet(如加载配置文件、创建数据库连接等),该方法仅执行一次。

  3. 服务:客户端每次访问Servlet时,服务器都会调用service(ServletRequest req, ServletResponse res)方法,该方法会根据请求方法(GET/POST)分发到对应的doGet()/doPost()方法,可执行多次。

  4. 销毁:服务器关闭时(或Servlet被移除时),调用destroy()方法,用于释放资源(如关闭数据库连接、释放缓存等),该方法仅执行一次。

生命周期核心方法:init()(初始化)、service()(处理请求)、destroy()(销毁),均定义在Servlet接口中,HttpServlet对service()方法进行了重写。

2.2.2 启动装入优先级

默认情况下,Servlet在第一次被客户端访问时才会被加载和实例化。若需要Servlet在服务器启动时就加载(提前初始化),可通过配置设置启动装入优先级。

配置方式:

  • web.xml配置:在<servlet>标签内添加<load-on-startup>数字</load-on-startup>,数字越小,优先级越高(数字为非负整数),例如:<load-on-startup>1</load-on-startup>。

  • 注解配置:在@WebServlet注解中添加loadOnStartup属性,例如:@WebServlet(urlPatterns = "/demo", loadOnStartup = 1)。

适用场景:需要提前初始化资源的Servlet(如加载全局配置、创建数据库连接池),避免第一次访问时因初始化耗时过长导致响应缓慢。

2.2.3 Servlet映射

Servlet映射是指将Servlet与一个或多个URL路径关联,客户端通过访问对应的URL,即可触发Servlet的service()方法,实现请求处理。映射的核心是"URL路径 → Servlet类"的对应关系。

映射方式(web.xml配置):

  1. 配置<servlet>标签:指定Servlet的名称(servlet-name)和对应的Servlet类(servlet-class)。

  2. 配置<servlet-mapping>标签:将Servlet名称(servlet-name)与URL路径(url-pattern)关联。

示例:

html 复制代码
<servlet>
    <servlet-name>DemoServlet</servlet-name>
    <servlet-class>com.example.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet</servlet-name>
    <url-pattern>/demo&lt;/url-pattern&gt;  <!-- 客户端访问路径:http://localhost:8080/项目名/demo -->
</servlet-mapping>

URL路径匹配规则:

注意:一个Servlet可以映射多个URL路径;URL路径匹配优先级:精确匹配 > 模糊匹配(/xxx/*) > 后缀匹配(*.xxx)。

2.2.4 Servlet注解配置

Servlet 3.0及以上版本支持注解配置,无需编写web.xml,直接通过@WebServlet注解即可完成Servlet的注册和映射,简化开发流程。

@WebServlet注解核心属性:

  • name:Servlet的名称(可选,默认是Servlet类的全限定名)。

  • urlPatterns:Servlet的URL映射路径(必选,可指定多个路径,如urlPatterns = {"/demo1", "/demo2"})。

  • loadOnStartup:启动装入优先级(可选,默认-1,即第一次访问时加载)。

  • initParams:Servlet的初始化参数(可选,如initParams = {@WebInitParam(name = "key", value = "value")})。

示例:

java 复制代码
@WebServlet(urlPatterns = {"/demo", "/test"}, loadOnStartup = 2, initParams = {@WebInitParam(name = "charset", value = "UTF-8")})
public class DemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 业务逻辑
    }
}

注意:注解配置与web.xml配置可共存,若两者配置了同一个Servlet,web.xml的配置会覆盖注解配置。

2.3 使用Servlet上下文

2.3.1 Servlet上下文对象

Servlet上下文(ServletContext)是Web应用的全局对象,代表整个Web应用,每个Web应用只有一个ServletContext对象,所有Servlet共享该对象。

核心作用:

  • 共享数据:在不同Servlet之间共享数据(通过setAttribute(String name, Object value)和getAttribute(String name)方法)。

  • 获取Web应用的全局信息:如Web应用的真实路径、初始化参数、MIME类型等。

  • 读取Web应用的资源文件:如配置文件(.properties)、静态资源(.html、.css)等。

获取ServletContext对象的方式:

  • 通过ServletConfig对象获取:config.getServletContext()(init()方法中可直接使用,因为init()方法的参数是ServletConfig)。

  • 通过HttpServlet获取:getServletContext()(HttpServlet类继承了GenericServlet,提供了该方法)。

  • 通过HttpServletRequest获取:request.getServletContext()。

注意:ServletContext的生命周期与Web应用一致,Web应用启动时创建,关闭时销毁;共享数据时需注意线程安全(多线程并发访问时需加锁)。

2.3.2 上下文初始化参数

上下文初始化参数是Web应用的全局参数,配置在整个Web应用范围内,所有Servlet都可以通过ServletContext对象获取,用于存储Web应用的全局配置(如数据库URL、编码格式等)。

配置方式:

  1. web.xml配置:在<web-app>标签内添加<context-param>标签,包含参数名(param-name)和参数值(param-value)。

  2. 注解配置:Servlet 3.0+支持通过@WebListener注解配合ServletContextListener实现,但更常用web.xml配置。

web.xml配置示例:

XML 复制代码
<web-app>
    <context-param>
        <param-name>dbUrl</param-name>
        <param-value>jdbc:mysql://localhost:3306/test</param-value>
    </context-param>
    <context-param>
        <param-name>charset</param-name>
        <param-value>UTF-8</param-value>
    </context-param>
</web-app>

获取方式:通过ServletContext对象的getInitParameter(String name)方法获取,示例:

java 复制代码
ServletContext context = getServletContext();
String dbUrl = context.getInitParameter("dbUrl"); // 获取数据库URL
String charset = context.getInitParameter("charset"); // 获取编码格式

区别:上下文初始化参数(全局)与Servlet初始化参数(局部):前者所有Servlet共享,后者仅当前Servlet可用;前者配置在<context-param>,后者配置在<servlet>标签内的<init-param>。

2.4 管理会话跟踪

2.4.1 实现会话跟踪

会话(Session)是指客户端与服务器之间的一次连续交互过程(从客户端第一次访问服务器开始,到客户端关闭浏览器或会话超时结束)。由于HTTP协议是无状态的,需通过会话跟踪技术记录客户端的状态信息。

常用会话跟踪方式:

  1. Cookie:客户端会话技术,将会话数据存储在客户端浏览器中,每次请求时自动携带Cookie数据到服务器。

  2. 核心方法:通过HttpServletResponse的addCookie(Cookie cookie)方法创建Cookie;通过HttpServletRequest的getCookies()方法获取Cookie。

  3. 特点:存储在客户端,安全性较低(可被篡改),存储容量有限(约4KB),可设置过期时间。

  4. Session:服务器端会话技术,将会话数据存储在服务器端,客户端通过Cookie(JSESSIONID)关联会话。

  5. 核心方法:通过HttpServletRequest的getSession()方法获取Session对象;通过setAttribute()/getAttribute()存储/获取会话数据;通过invalidate()销毁会话。

  6. 特点:存储在服务器端,安全性较高,存储容量无限制(受服务器内存影响),默认会话超时时间为30分钟(可配置)。

Session工作流程:

  1. 客户端第一次访问服务器时,服务器创建Session对象,生成唯一的JSESSIONID,并通过Cookie发送给客户端。

  2. 客户端后续访问时,浏览器自动携带JSESSIONID的Cookie,服务器通过JSESSIONID找到对应的Session对象,实现状态保持。

  3. 客户端关闭浏览器,JSESSIONID的Cookie失效,服务器端Session对象仍存在,直到超时后被销毁。

2.4.2 URL重写

URL重写是会话跟踪的补充方式,当客户端浏览器禁用Cookie时,无法通过Cookie携带JSESSIONID,此时可通过URL重写将JSESSIONID拼接在URL路径后,实现Session关联。

核心原理:将JSESSIONID作为URL的参数拼接,格式为:URL?JSESSIONID=会话ID,服务器通过URL参数获取JSESSIONID,找到对应的Session对象。

实现方式:使用HttpServletResponse的encodeURL(String url)或encodeRedirectURL(String url)方法,自动拼接JSESSIONID(若客户端禁用Cookie,则拼接;若启用,则不拼接)。

示例:

java 复制代码
// 重写URL,用于普通跳转
String url = response.encodeURL("/demo/user");
response.getWriter().write("<a href='" + url + "'>访问用户页面</a>");

// 重写URL,用于重定向
String redirectUrl = response.encodeRedirectURL("/demo/login");
response.sendRedirect(redirectUrl);

注意事项:URL重写需对所有跳转链接进行处理,否则会导致Session失效;URL重写会暴露JSESSIONID,安全性较低,仅在Cookie禁用时使用。

2.5 实现转发与重定向

2.5.1 请求转发

请求转发(Forward)是服务器内部的资源跳转方式,客户端发送一次请求,服务器将请求转发到另一个Servlet或页面,由目标资源处理后,将响应结果返回给客户端,客户端地址栏不变。

核心特点:

  • 仅客户端发送一次请求,服务器内部跳转,地址栏URL不变。

  • 转发的资源必须是当前Web应用内的资源(不能转发到外部URL)。

  • 共享request和response对象,转发前后的Servlet可通过request共享数据。

实现方式:通过RequestDispatcher对象的forward()方法,获取RequestDispatcher对象有两种方式:

  • request.getRequestDispatcher(String path):path为目标资源的相对路径(如"/demo/target")。

  • servletContext.getRequestDispatcher(String path):path为目标资源的绝对路径(以/开头,如"/demo/target")。

示例:

java 复制代码
// 在DemoServlet中转发到TargetServlet
RequestDispatcher dispatcher = request.getRequestDispatcher("/target");
// 转发请求和响应对象
dispatcher.forward(request, response);

2.5.2 请求重定向

请求重定向(Redirect)是服务器通知客户端重新向另一个URL发送请求的跳转方式,客户端发送两次请求,第一次请求服务器后,服务器返回302状态码和目标URL,客户端再向目标URL发送第二次请求,地址栏URL会变为目标URL。

核心特点:

  • 客户端发送两次请求,服务器不共享request和response对象,无法通过request共享数据。

  • 可重定向到当前Web应用内的资源,也可重定向到外部URL(如百度、谷歌)。

  • 地址栏URL会发生变化,显示目标资源的URL。

实现方式:通过HttpServletResponse的sendRedirect(String url)方法,url为目标资源的URL(可绝对路径或相对路径)。

示例:

java 复制代码
// 重定向到当前Web应用内的login页面
response.sendRedirect("/demo/login");

// 重定向到外部URL(百度)
response.sendRedirect("https://www.baidu.com");

转发与重定向的核心区别:

对比项 请求转发(Forward) 请求重定向(Redirect)
请求次数 1次 2次
地址栏变化 不变 变化
资源范围 仅当前Web应用 可跨Web应用、外部URL
request共享 可共享 不可共享
状态码 200(正常响应) 302(重定向)

2.6 使用过滤器与监听器

2.6.1 使用过滤器

过滤器(Filter)是Java EE的核心组件之一,运行在Servlet之前,用于对客户端请求和服务器响应进行拦截和处理,可实现统一的请求过滤(如编码设置、权限控制、日志记录等)。

核心作用:

  • 请求拦截:拦截客户端发送的请求,对请求参数、请求头进行处理(如统一设置请求编码)。

  • 响应拦截:拦截服务器返回的响应,对响应内容、响应头进行处理(如统一设置响应编码)。

  • 权限控制:判断客户端是否有权限访问目标资源(如未登录用户拦截到登录页面)。

  • 日志记录:记录请求的URL、访问时间、客户端IP等信息,用于系统监控。

过滤器开发步骤:

  1. 创建Java类,实现Filter接口。

  2. 重写Filter接口的三个方法:init(FilterConfig config)(初始化,仅执行一次)、doFilter(ServletRequest req, ServletResponse res, FilterChain chain)(核心过滤逻辑,每次请求执行)、destroy()(销毁,释放资源)。

  3. 配置过滤器(通过web.xml或@WebFilter注解),指定过滤的URL路径。

核心方法说明:doFilter()方法中,需调用chain.doFilter(req, res)方法,将请求传递给下一个过滤器或目标Servlet;若不调用该方法,则请求被拦截,无法到达目标资源。

注解配置示例:

java 复制代码
// 拦截所有URL路径,统一设置编码
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 统一设置请求和响应编码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        // 传递请求到下一个过滤器或Servlet
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 销毁操作
    }
}

2.6.2 使用监听器

监听器(Listener)是Java EE的核心组件之一,用于监听Web应用中特定对象的创建、销毁或属性变化,当事件发生时,触发监听器的对应方法,实现事件驱动编程。

常用监听器类型(按监听对象分类):

  1. ServletContext监听器(ServletContextListener):监听ServletContext对象的创建和销毁(Web应用启动和关闭),常用於初始化全局资源(如数据库连接池)。

  2. Session监听器(HttpSessionListener):监听Session对象的创建和销毁,用于统计在线人数。

  3. Request监听器(ServletRequestListener):监听ServletRequest对象的创建和销毁,用于记录请求的生命周期。

  4. 属性监听器(如ServletContextAttributeListener):监听对象的属性变化(添加、移除、修改)。

监听器开发步骤:

  1. 创建Java类,实现对应的监听器接口(如ServletContextListener)。

  2. 重写接口中的抽象方法(如contextInitialized()、contextDestroyed())。

  3. 配置监听器(通过web.xml或@WebListener注解)。

注解配置示例(ServletContextListener):

java 复制代码
@WebListener
public class MyServletContextListener implements ServletContextListener {
    // Web应用启动时触发(ServletContext创建)
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 初始化数据库连接池
        ServletContext context = sce.getServletContext();
        context.setAttribute("dbPool", new DBConnectionPool());
        System.out.println("Web应用启动,初始化数据库连接池");
    }

    // Web应用关闭时触发(ServletContext销毁)
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 关闭数据库连接池
        ServletContext context = sce.getServletContext();
        DBConnectionPool dbPool = (DBConnectionPool) context.getAttribute("dbPool");
        dbPool.close();
        System.out.println("Web应用关闭,销毁数据库连接池");
    }
}

2.7 代码示例

示例1:基础HttpServlet实现(处理GET/POST请求)

java 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 注解配置Servlet,映射路径为/demo
@WebServlet(urlPatterns = "/demo", loadOnStartup = 1)
public class DemoServlet extends HttpServlet {
    // 处理GET请求
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置请求和响应编码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        // 获取请求参数
        String name = request.getParameter("name");
        if (name == null || name.isEmpty()) {
            name = "匿名用户";
        }

        // 输出响应内容
        PrintWriter out = response.getWriter();
        out.write("<html><head><title>Servlet示例</title></head>");
        out.write("<body><h1>欢迎您," + name + "!</h1></body></html>");
        out.close();
    }

    // 处理POST请求(复用GET请求逻辑)
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

代码解析

1. 导入依赖包(必须的核心类)

java 复制代码
import javax.servlet.ServletException; // Servlet 运行异常
import javax.servlet.annotation.WebServlet; // Servlet 注解配置(3.0+新特性)
import javax.servlet.http.HttpServlet; // HTTP 协议的 Servlet 基类
import javax.servlet.http.HttpServletRequest; // 封装浏览器的请求对象
import javax.servlet.http.HttpServletResponse; // 封装服务器的响应对象
import java.io.IOException; // IO 流异常
import java.io.PrintWriter; // 响应输出流(向浏览器写数据)

这些是 Servlet 开发的基础依赖,负责处理 HTTP 请求、响应、异常、输出内容。

2. 核心注解:配置 Servlet(关键)

java 复制代码
// 注解配置Servlet,映射路径为/demo
@WebServlet(urlPatterns = "/demo", loadOnStartup = 1)
  • @WebServletServlet 3.0 注解 ,替代传统的 web.xml 配置,直接标记这是一个 Servlet 类。
  • urlPatterns = "/demo"访问路径 ,浏览器通过 http://服务器地址/项目名/demo 访问这个 Servlet。
  • loadOnStartup = 1:服务器启动时立即初始化该 Servlet(默认是第一次访问才初始化,数字越小优先级越高)。

3. 定义 Servlet 类

java 复制代码
public class DemoServlet extends HttpServlet {
  • DemoServlet:自定义的 Servlet 类名。
  • extends HttpServlet必须继承 HttpServlet,这是处理 HTTP 协议请求的标准基类。

4. 处理 GET 请求(核心方法)

java 复制代码
// 处理GET请求
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  • @Override:重写父类 HttpServletdoGet 方法,专门处理浏览器的 GET 请求(比如直接输入网址、超链接访问)。
  • 两个核心参数:
    • HttpServletRequest request请求对象,封装浏览器发送的所有数据(参数、请求头、IP 等)。
    • HttpServletResponse response响应对象,服务器通过它向浏览器返回数据。
  • throws:声明可能抛出的异常(Servlet 运行异常、IO 异常)。

4.1 设置编码(解决中文乱码)

java 复制代码
// 设置请求和响应编码
request.setCharacterEncoding("UTF-8"); // 解析请求参数的编码(解决中文参数乱码)
response.setContentType("text/html;charset=UTF-8"); // 告诉浏览器:返回HTML格式,UTF-8编码

这两行是中文乱码的解决方案,必须加,否则中文会显示问号。

4.2 获取请求参数

java 复制代码
// 获取请求参数
String name = request.getParameter("name"); // 获取浏览器传递的 name 参数
if (name == null || name.isEmpty()) { // 判断参数是否为空
    name = "匿名用户"; // 空值默认赋值
}
  • request.getParameter("name"):获取 URL / 表单中名为 name 的参数。例:访问 http://.../demo?name=张三,会获取到 张三
  • 空值判断:如果用户没传参数,默认显示「匿名用户」。

4.3 向浏览器输出 HTML 页面

java 复制代码
// 输出响应内容
PrintWriter out = response.getWriter(); // 获取响应输出流
out.write("<html><head><title>Servlet示例</title></head>"); // 写HTML代码
out.write("<body><h1>欢迎您," + name + "!</h1></body></html>");
out.close(); // 关闭流
  • PrintWriter:输出流,负责把数据写给浏览器。
  • out.write():直接输出 HTML 代码,浏览器会解析并渲染页面。

5. 处理 POST 请求

java 复制代码
// 处理POST请求(复用GET请求逻辑)
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
}
  • doPost:专门处理浏览器的 POST 请求(比如表单提交)。
  • 直接调用 doGet:让 GET 和 POST 请求共用一套逻辑,避免重复写代码。

代码运行效果

  1. 把代码部署到 Tomcat 等服务器;
  2. 浏览器访问:
    • 无参数:http://localhost:8080/项目名/demo → 显示:欢迎您,匿名用户!
    • 带参数:http://localhost:8080/项目名/demo?name=小明 → 显示:欢迎您,小明!

核心知识点总结

  1. Servlet 作用:Java 用于处理 Web 请求、返回响应的标准组件。
  2. 注解配置@WebServlet 替代 XML,简化配置。
  3. 请求 / 响应对象request 拿数据,response 返数据。
  4. 编码设置:UTF-8 是解决中文乱码的标准方案。
  5. 请求方法doGet/doPost 分别处理 GET/POST 请求,可复用逻辑。

示例2:Session会话跟踪示例

java 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/sessionDemo")
public class SessionDemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        // 获取Session对象(若不存在则创建)
        HttpSession session = request.getSession();

        // 存储会话数据
        session.setAttribute("username", "admin");

        // 获取会话ID和超时时间
        String sessionId = session.getId();
        int maxInactiveInterval = session.getMaxInactiveInterval(); // 单位:秒,默认1800秒(30分钟)

        out.write("<h1>Session示例</h1>");
        out.write("<p>会话ID:" + sessionId + "</p>");
        out.write("<p>会话超时时间:" + maxInactiveInterval + "秒</p>");
        out.write("<p>当前登录用户:" + session.getAttribute("username") + "</p>");

        // 销毁会话(可选)
        // session.invalidate();
        out.close();
    }
}

代码解析

1. 导入依赖包

java 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; // 【新增核心】会话对象专用类
import java.io.IOException;
import java.io.PrintWriter;
  • 相比上一段代码,新增了 HttpSession 导入,这是操作会话的核心类;

  • 其余包:负责 Servlet 基础功能、请求 / 响应、IO 输出、异常处理,作用和之前一致。


2. Servlet 注解配置

java 复制代码
@WebServlet("/sessionDemo")
  • 注解配置访问路径:浏览器通过 http://服务器地址/项目名/sessionDemo 访问该 Servlet;

  • 简化写法:省略urlPatterns,直接写路径,等价于 urlPatterns = "/sessionDemo"

3. 定义 Servlet 类

java 复制代码
public class SessionDemoServlet extends HttpServlet {
  • 继承 HttpServlet:标准 HTTP 请求处理基类,必须继承。

4. 处理 GET 请求(核心逻辑)

java 复制代码
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  • 重写doGet:处理浏览器 GET 请求(直接访问网址);

  • request:请求对象,response:响应对象。

4.1 响应基础配置

java 复制代码
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
  • 设置响应内容为HTML格式,编码UTF-8(解决中文乱码);

  • 获取输出流out,用于向浏览器打印网页内容。

4.2 获取 Session 对象(核心步骤)

java 复制代码
// 获取Session对象(若不存在则创建)
HttpSession session = request.getSession();
  • request.getSession()最核心方法

    • 作用:检查当前浏览器是否有对应的会话

    • 规则:有则直接获取,无则自动创建新会话

    • 等价写法:request.getSession(true)

4.3 存储会话数据

java 复制代码
// 存储会话数据
session.setAttribute("username", "admin");
  • setAttribute(键, 值):向会话域存储数据;

  • 特点:数据是会话级共享的(当前浏览器的所有请求 / 页面都能访问),常用于存储登录用户名、用户权限等。

4.4 获取会话核心信息

java 复制代码
// 获取会话ID和超时时间
String sessionId = session.getId();
int maxInactiveInterval = session.getMaxInactiveInterval(); // 单位:秒
  • session.getId():获取唯一会话 ID,服务器自动生成,用于区分不同用户的会话;

  • getMaxInactiveInterval():获取会话超时时间 ,单位秒,默认 1800 秒(30 分钟),用户 30 分钟无操作,会话自动销毁。

4.5 输出网页内容

java 复制代码
out.write("<h1>Session示例</h1>");
out.write("<p>会话ID:" + sessionId + "</p>");
out.write("<p>会话超时时间:" + maxInactiveInterval + "秒</p>");
out.write("<p>当前登录用户:" + session.getAttribute("username") + "</p>");
  • session.getAttribute("username"):从会话中读取之前存储的用户名;

  • 直接输出 HTML,浏览器会渲染成可视化页面。

4.6 手动销毁会话(可选)

java 复制代码
// 销毁会话(可选)
// session.invalidate();
  • session.invalidate()立即销毁当前会话,清除所有会话数据;

  • 场景:用户点击退出登录时,必须调用此方法。

4.7 关闭输出流

java 复制代码
out.close();
  • 释放 IO 资源,标准收尾操作。

5. 处理 POST 请求(省略)

这段代码没有重写doPost ,因此无法处理 POST 请求(如表单提交),如果需要,直接复用doGet即可。


代码运行效果

部署到 Tomcat 服务器后,浏览器访问:http://localhost:8080/项目名/sessionDemo

页面会显示:

复制代码
Session示例
会话ID:xxxxxxxxxxxxxxxxxxxxxxx
会话超时时间:1800秒
当前登录用户:admin

核心知识点总结

  1. HttpSession 作用 :服务器端的会话技术,用于跨请求 / 跨页面共享用户数据(最常用场景:用户登录状态保持);

  2. 核心方法

    • request.getSession():创建 / 获取会话

    • setAttribute()/getAttribute():存 / 取会话数据

    • getId():获取唯一会话 ID

    • invalidate():销毁会话

  3. 生命周期:默认 30 分钟无操作自动失效,关闭浏览器(Cookie 失效)、手动销毁都会结束会话;

  4. 作用域会话级,整个浏览器与服务器的对话期间,数据都有效。

示例3:过滤器(统一编码设置)

java 复制代码
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

// 拦截所有请求,统一设置编码
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
    private String charset;

    // 初始化:读取上下文初始化参数
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 获取上下文初始化参数中的编码格式,默认UTF-8
        charset = filterConfig.getServletContext().getInitParameter("charset");
        if (charset == null) {
            charset = "UTF-8";
        }
    }

    // 核心过滤逻辑
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 设置请求编码
        request.setCharacterEncoding(charset);
        // 设置响应编码
        response.setContentType("text/html;charset=" + charset);
        // 传递请求到下一个过滤器或Servlet
        chain.doFilter(request, response);
    }

    // 销毁:释放资源
    @Override
    public void destroy() {
        charset = null;
    }
}

代码解析

1. 导入依赖包

java 复制代码
import javax.servlet.*;  // 导入过滤器核心接口(Filter、FilterChain、FilterConfig等)
import javax.servlet.annotation.WebFilter; // 过滤器注解配置(3.0+新特性)
import java.io.IOException; // IO异常
  • Filter:过滤器核心接口,必须实现;

  • WebFilter:注解,用于配置过滤器的拦截路径;

  • 其余为异常、IO 基础包。

2. 过滤器注解配置(关键)

java 复制代码
// 拦截所有请求,统一设置编码
@WebFilter(urlPatterns = "/*")
  • @WebFilter:标记当前类为过滤器 ,替代传统 web.xml 配置;

  • urlPatterns = "/*"拦截规则/* 表示拦截项目中所有的请求(所有 Servlet、页面、静态资源);

  • 这是全局编码过滤器的标准配置。

3. 定义过滤器类

java 复制代码
public class EncodingFilter implements Filter {
  • EncodingFilter:自定义过滤器类名;

  • implements Filter必须实现 javax.servlet.Filter 接口,强制重写 3 个核心方法。

4. 成员变量

java 复制代码
private String charset;
  • 用于存储编码格式(如 UTF-8),全局供过滤器使用。

5. 初始化方法:init ()

java 复制代码
// 初始化:服务器启动时执行1次
@Override
public void init(FilterConfig filterConfig) throws ServletException {
    // 从全局配置中读取编码参数
    charset = filterConfig.getServletContext().getInitParameter("charset");
    // 如果没配置,默认使用UTF-8
    if (charset == null) {
        charset = "UTF-8";
    }
}

核心说明:

  1. 执行时机服务器启动时自动执行 1 次,过滤器初始化;

  2. 参数作用

    • FilterConfig:过滤器配置对象;

    • getServletContext():获取全局上下文对象(整个 Web 项目共享);

    • getInitParameter("charset"):读取全局配置的编码参数 (可在 web.xml 配置);

  3. 容错处理 :未配置编码时,默认使用 UTF-8


6. 核心过滤方法:doFilter ()(最重要)

java 复制代码
// 核心过滤逻辑:每次请求都会执行
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    // 1. 设置请求编码(解决前端传递中文参数乱码)
    request.setCharacterEncoding(charset);
    // 2. 设置响应编码(解决后端返回中文乱码)
    response.setContentType("text/html;charset=" + charset);
    // 3. 放行请求:交给下一个过滤器/目标Servlet处理(必须写!)
    chain.doFilter(request, response);
}

核心说明:

  1. 执行时机每次前端请求都会执行(拦截→处理→放行);

  2. 核心逻辑

    • 统一设置请求编码:解析前端传递的中文参数;

    • 统一设置响应编码:后端返回 HTML / 中文数据不乱码;

  3. 必须代码chain.doFilter(request, response)

    • 作用:放行请求,如果不写,请求会被拦截卡住,无法到达 Servlet;
  4. 参数

    • request/response:请求 / 响应对象;

    • FilterChain:过滤器链,管理多个过滤器的执行顺序。


7. 销毁方法:destroy ()

java 复制代码
// 销毁:服务器关闭时执行1次
@Override
public void destroy() {
    charset = null; // 释放资源
}
  • 执行时机服务器关闭时自动执行 1 次

  • 作用:释放过滤器占用的资源,是标准收尾操作。


过滤器执行流程(必懂)

复制代码
浏览器发送请求
    ↓
拦截所有请求 → EncodingFilter.doFilter()
    ↓
统一设置请求/响应编码
    ↓
chain.doFilter() 放行请求
    ↓
请求到达对应的 Servlet 处理
    ↓
Servlet 返回响应
    ↓
过滤器收尾 → 响应返回浏览器

对比:过滤器 vs 手动编码

方式 优点 缺点
每个 Servlet 手动写 简单(新手常用) 代码冗余、维护麻烦
编码过滤器 全局统一、无冗余、易维护 需理解 Filter 机制

核心知识点总结

  1. Filter 作用 :拦截 Web 请求,实现统一预处理(编码、登录校验、日志等);

  2. 三个核心方法

    • init():服务器启动初始化(1 次);

    • doFilter():每次请求执行(核心逻辑);

    • destroy():服务器关闭销毁(1 次);

  3. 关键配置@WebFilter("/*") 拦截所有请求;

  4. 核心代码chain.doFilter() 必须调用,否则请求无法放行;

  5. 核心价值全局统一解决中文乱码,是 Java Web 开发的标准解决方案。

示例4:请求转发与重定向示例

java 复制代码
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/forwardRedirectDemo")
public class ForwardRedirectDemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 请求转发示例
        // 存储数据到request域(转发可共享)
        request.setAttribute("msg", "请求转发的数据");
        // 获取RequestDispatcher对象,转发到targetServlet
        RequestDispatcher dispatcher = request.getRequestDispatcher("/targetServlet");
        dispatcher.forward(request, response);

        // 2. 请求重定向示例(注释转发代码,启用重定向)
        // response.sendRedirect("/demo/targetServlet");
    }
}

// 目标Servlet,接收转发或重定向的请求
@WebServlet("/targetServlet")
class TargetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        // 获取request域中的数据(仅转发可获取,重定向无法获取)
        String msg = (String) request.getAttribute("msg");
        response.getWriter().write("<h1>目标Servlet</h1>");
        response.getWriter().write("<p>获取到的数据:" + (msg == null ? "无(可能是重定向)" : msg) + "</p>");
    }
}

代码解释

1. 导入依赖包

java 复制代码
import javax.servlet.RequestDispatcher; // 【核心】请求转发专用对象
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
  • 新增 RequestDispatcher请求转发的核心工具类

  • 其余为 Servlet 基础包,作用和之前一致。

2. 源 Servlet(跳转发起方)

java 复制代码
// 访问路径:/forwardRedirectDemo
@WebServlet("/forwardRedirectDemo")
public class ForwardRedirectDemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        // ========== 1. 请求转发(默认启用)==========
        // 向request域存储数据(仅转发可共享)
        request.setAttribute("msg", "请求转发的数据");
        // 获取转发器,指定跳转目标:/targetServlet
        RequestDispatcher dispatcher = request.getRequestDispatcher("/targetServlet");
        // 执行转发(服务器内部跳转)
        dispatcher.forward(request, response);

        // ========== 2. 请求重定向(默认注释,需手动启用)==========
        // 注释上方转发代码,取消注释此行即可启用重定向
        // response.sendRedirect("/demo/targetServlet");
    }
}

1.1 请求转发(核心)

java 复制代码
// 1. 存储数据到 request 域(请求域)
request.setAttribute("msg", "请求转发的数据");
// 2. 获取转发器
RequestDispatcher dispatcher = request.getRequestDispatcher("/targetServlet");
// 3. 执行转发
dispatcher.forward(request, response);
  • request.setAttribute() :向请求域 存数据,只有转发能共享这个数据

  • getRequestDispatcher("/目标路径"):指定要跳转到的目标 Servlet;

  • forward()服务器内部跳转,浏览器无感知。

1.2 请求重定向(注释状态)

复制代码
response.sendRedirect("/demo/targetServlet");
  • sendRedirect()客户端跳转,告诉浏览器重新访问新地址;

  • 注释转发代码才能启用重定向。


3. 目标 Servlet(跳转接收方)

java 复制代码
// 目标Servlet,接收转发/重定向的请求
@WebServlet("/targetServlet")
class TargetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        
        // 尝试获取源Servlet存储的数据
        String msg = (String) request.getAttribute("msg");
        
        // 响应输出结果
        response.getWriter().write("<h1>目标Servlet</h1>");
        response.getWriter().write("<p>获取到的数据:" + (msg == null ? "无(可能是重定向)" : msg) + "</p>");
    }
}
  • 尝试获取 request 域中的 msg 数据;

  • 三元运算符判断:

    • 有数据 → 是请求转发

    • 无数据 → 是请求重定向


运行效果

情况 1:启用【请求转发】(默认代码)

  1. 浏览器访问:http://localhost:8080/项目名/forwardRedirectDemo

  2. 地址栏不变,仍显示源地址;

  3. 页面输出:

    复制代码
    目标Servlet
    获取到的数据:请求转发的数据

情况 2:启用【请求重定向】(注释转发,打开重定向)

  1. 浏览器访问:http://localhost:8080/项目名/forwardRedirectDemo

  2. 地址栏变为目标地址http://localhost:8080/项目名/targetServlet

  3. 页面输出:

    复制代码
    目标Servlet
    获取到的数据:无(可能是重定向)

核心区别(必背)

特性 请求转发 (forward) 请求重定向 (redirect)
跳转执行者 服务器内部 浏览器客户端
请求次数 1 次 2 次
地址栏变化 不变 变为目标地址
request 数据共享 可以共享 不可以共享
跳转范围 只能跳转到项目内部 可跳转到外部网站
核心 API forward() sendRedirect()

核心知识点总结

  1. 请求转发 :服务器内部跳转,共享请求数据,地址栏不变,适合页面内部跳转、数据传递;

  2. 请求重定向 :浏览器重新发起请求,不共享数据,地址栏改变,适合登录后跳首页、外部链接;

  3. request :仅在一次请求内有效,转发是一次请求,重定向是两次请求,所以重定向拿不到数据。

Servlet 入门实战项目:简易用户登录系统

项目目标

  1. 掌握 HttpServlet 处理请求、HTTP 协议应用
  2. 理解 Servlet 生命周期、注解配置、映射规则
  3. 学会使用 ServletContext 上下文、会话跟踪
  4. 区分请求转发重定向
  5. 掌握过滤器 统一编码、监听器统计在线人数
  6. 完成一个可运行的 Web 小项目

项目环境

  • JDK 8+
  • Tomcat 9/10(推荐 9,兼容原生 Servlet)
  • IDEA/Eclipse(任意 Java 开发工具)
  • 纯 Web 项目,无需数据库(使用固定账号密码,降低难度)

一、项目功能

  1. 登录页面:输入用户名 / 密码登录
  2. 登录验证:Servlet 校验账号密码
  3. 首页展示:显示登录用户信息、在线人数
  4. 用户注销:销毁会话,退出登录
  5. 全局编码过滤:解决中文乱码
  6. 在线人数统计:监听器实时统计在线用户数

二、项目目录结构

复制代码
Web项目
├── src
│   └── com
│       ├── servlet        // Servlet核心类
│       │   ├── LoginServlet.java    // 登录处理
│       │   ├── IndexServlet.java    // 首页
│       │   └── LogoutServlet.java   // 注销处理
│       ├── filter         // 过滤器
│       │   └── EncodingFilter.java  // 编码过滤器
│       └── listener       // 监听器
│           └── OnlineListener.java  // 在线人数统计
├── webapp
│   ├── login.html         // 登录页面
│   └── 404.html           // 错误页面
└── WEB-INF
    └── web.xml            // 全局配置文件

三、分步实现(逐行代码,直接复制运行)

1. 全局配置文件(web.xml)

作用 :配置上下文初始化参数、欢迎页,对应Servlet 上下文知识点

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 上下文初始化参数(全局编码) -->
    <context-param>
        <param-name>charset</param-name>
        <param-value>UTF-8</param-value>
    </context-param>

    <!-- 欢迎页 -->
    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>
</web-app>

2. 编码过滤器(EncodingFilter.java)

作用 :统一设置请求 / 响应编码,解决中文乱码,对应过滤器知识点

java 复制代码
package com.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

// 拦截所有请求 /*
@WebFilter("/*")
public class EncodingFilter implements Filter {
    private String charset;

    // 初始化:读取全局上下文参数
    @Override
    public void init(FilterConfig filterConfig) {
        charset = filterConfig.getServletContext().getInitParameter("charset");
        if (charset == null) charset = "UTF-8";
    }

    // 核心过滤方法
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 设置编码
        request.setCharacterEncoding(charset);
        response.setContentType("text/html;charset=" + charset);
        // 放行请求
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {}
}

3. 在线人数监听器(OnlineListener.java)

作用 :监听 Session 创建 / 销毁,统计在线人数,对应监听器知识点

java 复制代码
package com.listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.ServletContext;

@WebListener
public class OnlineListener implements HttpSessionListener {
    // 会话创建:在线人数+1
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        ServletContext context = se.getSession().getServletContext();
        Integer count = (Integer) context.getAttribute("onlineCount");
        if (count == null) count = 0;
        context.setAttribute("onlineCount", count + 1);
    }

    // 会话销毁:在线人数-1
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        ServletContext context = se.getSession().getServletContext();
        Integer count = (Integer) context.getAttribute("onlineCount");
        if (count != null && count > 0) {
            context.setAttribute("onlineCount", count - 1);
        }
    }
}

4. 登录页面(login.html)

作用:前端表单提交请求,纯 HTML,简单易用

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
    <h2>Servlet登录系统</h2>
    <form action="login" method="post">
        用户名:<input type="text" name="username" required><br><br>
        密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password" required><br><br>
        <button type="submit">登录</button>
    </form>
</body>
</html>

5. 登录处理 Servlet(LoginServlet.java)

作用 :处理登录请求、会话跟踪、转发 / 重定向,覆盖HttpServlet、生命周期、注解配置

java 复制代码
package com.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

// 注解配置:映射路径、启动优先级
@WebServlet(value = "/login", loadOnStartup = 1)
public class LoginServlet extends HttpServlet {
    // 固定账号密码(无数据库,简化学习)
    private static final String USERNAME = "admin";
    private static final String PASSWORD = "123456";

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 2. 校验账号密码
        if (USERNAME.equals(username) && PASSWORD.equals(password)) {
            // 登录成功:创建Session,存储用户信息(会话跟踪)
            HttpSession session = request.getSession();
            session.setAttribute("user", username);
            
            // URL重写:兼容浏览器禁用Cookie
            String url = response.encodeRedirectURL("index");
            // 重定向到首页
            response.sendRedirect(url);
        } else {
            // 登录失败:请求转发,携带错误信息
            request.setAttribute("msg", "用户名或密码错误!");
            request.getRequestDispatcher("login.html").forward(request, response);
        }
    }
}

6. 首页 Servlet(IndexServlet.java)

作用:展示用户信息、在线人数,验证登录状态

java 复制代码
package com.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        HttpSession session = request.getSession();
        String user = (String) session.getAttribute("user");

        // 未登录:重定向到登录页
        if (user == null) {
            response.sendRedirect("login.html");
            return;
        }

        // 获取在线人数(上下文共享数据)
        Integer onlineCount = (Integer) getServletContext().getAttribute("onlineCount");

        // 输出页面
        out.write("<h2>欢迎您," + user + "!</h2>");
        out.write("<p>当前在线人数:" + onlineCount + "</p>");
        out.write("<a href='logout'>退出登录</a>");
    }
}

7. 注销 Servlet(LogoutServlet.java)

作用:销毁会话,退出登录

java 复制代码
package com.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 销毁Session
        request.getSession().invalidate();
        // 重定向到登录页
        response.sendRedirect("login.html");
    }
}

四、项目运行说明

  1. 将项目部署到 Tomcat
  2. 启动 Tomcat,访问:http://localhost:8080/项目名/
  3. 登录账号:admin,密码:123456
  4. 测试功能:登录、查看在线人数、退出登录

学习模块与项目中对应的实现表

学习模块 项目中对应的实现
处理客户端请求 LoginServlet/IndexServlet 继承 HttpServlet,处理 GET/POST 请求
Servlet 生命周期 & 配置 注解 @WebServlet、loadOnStartup 启动优先级、URL 映射
Servlet 上下文 web.xml 配置全局参数、共享在线人数数据
会话跟踪 Session 存储用户信息、URL 重写兼容 Cookie 禁用
转发与重定向 登录失败转发、登录成功 / 注销重定向
过滤器 & 监听器 EncodingFilter 编码过滤、OnlineListener 统计在线人数
相关推荐
奋发向前wcx3 小时前
y1,y2总复习笔记5 2026.7.19
数据结构·笔记·算法
@Mike@5 小时前
02-数据库学习笔记(SQL引擎)
数据库·笔记·学习
六点_dn6 小时前
RabbitMQ学习笔记-定义与作用
笔记·学习·rabbitmq
我的xiaodoujiao6 小时前
快速学习Python基础知识详细图文教程9--函数进阶
开发语言·python·学习·测试工具
世人万千丶8 小时前
鸿蒙Flutter Flexible与Expanded的区别
学习·flutter·harmonyos·鸿蒙
香辣牛肉饭8 小时前
【算法】动态规划 最长公共子序列(LCS)
经验分享·笔记·算法·动态规划
迷途呀8 小时前
Python:函数中的参数类型
开发语言·笔记·python·langchain·nlp
YM52e8 小时前
鸿蒙Flutter Center居中组件:Align对齐详解
android·学习·flutter·华为·harmonyos·鸿蒙
依然范特东9 小时前
动手学深度学习笔记--卷积层、微调
人工智能·笔记·深度学习