一、Servlet核心:加载顺序+完整代码修正

Servlet是Java Web的核心组件,负责接收客户端HTTP请求、处理业务逻辑并返回响应结果,<load-on-startup>是控制Servlet加载时机的关键配置,也是后端面试的高频考点,精准掌握加载规则能有效优化Web应用启动性能。

Servlet是Java Web的核心组件,负责处理客户端请求并返回响应,<load-on-startup>是控制Servlet加载时机的关键配置,也是面试常考点。

1.  Servlet加载顺序:load-on-startup

该标签用于配置Servlet在Web容器(如Tomcat)中的加载时机,默认情况下Servlet是首次访问时才加载,通过该标签可实现容器启动时预加载。

  • 取值为负数(默认-1):Servlet在首次被客户端访问时加载、初始化

  • 取值为0或正整数:Web容器启动时立即加载,数值越小,加载优先级越高;数值相同则按照web.xml中的配置顺序加载

<!-- web.xml中Servlet配置示例 -->
<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.demo.LoginServlet</servlet-class>
    <!-- 容器启动时加载,优先级1,数值越小越早加载 -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

2. 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 java.io.IOException;

/**
 * 登录Servlet,处理登录请求
 * 注解@WebServlet替代web.xml的映射配置,简化开发
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    /**
     * 处理GET请求:一般用于页面跳转、数据查询
     * 注意:必须删除super.doGet(),父类默认返回405方法不允许错误
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置响应编码,解决中文乱码
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("请使用POST方式提交登录表单!");
    }

    /**
     * 处理POST请求:一般用于表单数据提交、敏感数据传输
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 设置请求编码,适配中文参数
        req.setCharacterEncoding("UTF-8");
        // 2. 通过name属性获取表单参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        // 控制台打印参数,调试使用
        System.out.println("前端传递用户名:" + username);
        System.out.println("前端传递密码:" + password);

        // 3. 设置响应编码,返回中文数据不乱码
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("登录请求接收成功!用户名:" + username);
    }
}

易错提醒:不要保留super.doGet(req, resp)和super.doPost(req, resp),父类HttpServlet的默认实现会返回405错误,导致请求失败!

二、Filter过滤器:统一编码处理+完整实现

Filter过滤器是Java Web的核心扩展组件,主要用于拦截客户端请求和服务器响应,统一处理通用业务逻辑,比如全局编码设置、接口权限校验、请求日志记录、非法请求过滤,最大的优势是避免每个Servlet单独编写重复逻辑,大幅减少代码冗余,也是解决中文乱码最便捷的方案。

Filter过滤器用于拦截客户端请求和服务器响应,统一处理通用逻辑,比如全局编码设置、权限校验、日志记录、请求过滤,避免每个Servlet单独处理编码,减少代码冗余。

1. 编码过滤器完整代码

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

/**
 * 全局编码过滤器,统一处理请求和响应编码
 * 拦截所有请求/*,覆盖所有页面和Servlet
 */
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
public class EncodingFilter implements Filter {
    // 定义编码格式,从配置文件读取
    private String encode;

    /**
     * 过滤器初始化:服务器启动时执行一次,读取初始化参数
     */
    @Override
    public void init(FilterConfig config) throws ServletException {
        // 获取web.xml中配置的编码参数
        this.encode = config.getInitParameter("encode");
        // 兜底逻辑:无配置时默认UTF-8,避免空指针
        if (encode == null || encode.isEmpty()) {
            this.encode = "UTF-8";
        }
        System.out.println("编码过滤器初始化完成,默认编码:" + encode);
    }

    /**
     * 核心过滤方法:每次请求都会执行
     * 执行顺序:过滤前逻辑 → 放行请求 → 执行业务代码 → 过滤后逻辑
     */
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("===== 编码过滤器开始拦截请求 =====");
        // 统一设置请求编码
        req.setCharacterEncoding(encode);
        // 统一设置响应编码和内容类型
        resp.setContentType("text/html;charset=" + encode);
        resp.setCharacterEncoding(encode);

        // 放行请求,执行后续过滤器或Servlet业务逻辑
        chain.doFilter(req, resp);

        System.out.println("===== 编码过滤器请求处理完毕 =====");
    }

    /**
     * 过滤器销毁:服务器关闭时执行一次
     */
    @Override
    public void destroy() {
        System.out.println("编码过滤器已销毁");
    }
}

2. web.xml配置过滤器初始化参数

如果不使用注解,可通过xml配置初始化编码参数,适配老项目开发:

<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>com.demo.EncodingFilter</filter-class>
    <!-- 初始化编码参数 -->
    <init-param>
        <param-name>encode</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <!-- 拦截所有请求 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

三、Listener监听器:Web容器生命周期监听

监听器用于监听Web容器三大域对象(ServletContext上下文、HttpSession会话、ServletRequest请求)的生命周期事件,无需手动调用,由Web容器自动触发,常用于全局数据初始化、在线用户人数统计、会话状态监控、项目启动加载配置等场景,是实现全局业务监听的核心工具。

监听器用于监听Web容器的生命周期事件(上下文、会话、请求的创建与销毁),常用于全局数据初始化、在线人数统计、会话状态监控等场景,无需手动调用,容器自动触发。

1. 通用监听器完整代码

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

/**
 * 全局监听器,监听三大域对象生命周期
 * 实现三个核心监听接口:上下文监听、会话监听、请求监听
 */
@WebListener
public class GlobalListener implements ServletContextListener, HttpSessionListener, ServletRequestListener {

    /**
     * 全局上下文初始化:服务器启动时执行,只执行一次
     * 常用于全局配置、数据初始化
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("【全局上下文】服务器启动,初始化完成");
        // 存储全局参数,整个项目共享
        sce.getServletContext().setAttribute("projectName", "JavaWeb基础Demo");
    }

    /**
     * 全局上下文销毁:服务器关闭时执行,只执行一次
     */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("【全局上下文】服务器关闭,销毁完成");
    }

    /**
     * 会话创建:用户首次访问项目时创建Session
     */
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("【会话】创建成功,SessionID:" + se.getSession().getId());
    }

    /**
     * 会话销毁:Session超时、手动注销时执行
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("【会话】销毁成功,SessionID:" + se.getSession().getId());
    }

    /**
     * 请求初始化:每次客户端请求都会触发
     */
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("【请求】接收到客户端请求,初始化完成");
    }

    /**
     * 请求销毁:请求响应完毕后触发
     */
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("【请求】响应完成,请求对象销毁");
    }
}

四、Java Web完整请求执行流程

理清请求完整执行顺序,才能彻底吃透Web容器底层运行机制,这也是面试中常被问到的核心问答题,清晰的执行链路能帮我们快速定位请求拦截、业务处理、响应返回过程中的问题,完整执行链路如下:

理清执行顺序,才能彻底理解Web容器运行机制,也是面试高频问答题,完整执行链路如下:

  1. 客户端发送HTTP请求到Web容器(Tomcat)

  2. 监听器触发:requestInitialized(请求初始化)

  3. 过滤器执行:doFilter前置逻辑(编码设置、拦截校验)

  4. 请求到达对应Servlet,执行doGet/doPost业务逻辑

  5. 过滤器执行:doFilter后置逻辑

  6. 监听器触发:requestDestroyed(请求销毁)

  7. 容器将响应结果返回给客户端

额外生命周期顺序:服务器启动 → 上下文初始化(contextInitialized) → 过滤器初始化 → 用户访问 → Session创建(sessionCreated) → 服务器关闭 → Session销毁 → 过滤器销毁 → 上下文销毁(contextDestroyed)

五、核心总结与易错点复盘

5.1 核心知识点总结

  • load-on-startup取值为负数(默认-1):Servlet首次访问时加载;取值为0或正整数:容器启动时预加载,数值越小优先级越高

  • Servlet核心避免405错误:必须删除父类doGet()、doPost()默认实现,自定义业务逻辑

  • Filter核心作用:统一拦截请求响应,chain.doFilter()是放行关键,缺少则请求无法流转

  • Listener核心作用:监听容器生命周期,自动触发,无需手动调用,实现全局业务监听

  • 中文乱码终极解决方案:通过Filter全局统一设置请求、响应UTF-8编码,无需单个Servlet配置

  • 请求执行固定顺序:监听器请求初始化 → 过滤器前置逻辑 → Servlet业务处理 → 过滤器后置逻辑 → 监听器请求销毁

  • id唯一供前端用,name供后端传参,表单必须加name

  • load-on-startup负数:首次访问加载;0/正数:启动加载,数值越小优先级越高

  • Filter统一处理全局逻辑,避免重复代码,chain.doFilter()必须写,否则请求无法放行

  • Listener监听容器生命周期,自动触发,无需手动调用

  • 中文乱码核心解决:请求和响应统一设置UTF-8编码,过滤器全局处理最便捷

5.2 高频易错点

  1. Servlet中保留super.doGet()/super.doPost(),父类默认返回405 Method Not Allowed错误

  2. 过滤器未配置urlPatterns拦截路径,导致过滤器完全不生效,无法拦截请求

  3. 过滤器忘记编写chain.doFilter(req, resp),请求被拦截无法到达Servlet,页面无响应

  4. 忽略全局编码配置,前端传递中文参数到后端出现乱码,响应返回中文也乱码

  5. 混淆Servlet加载规则,误以为load-on-startup数值越大优先级越高

  6. Servlet中保留super.doGet(),导致405错误

  7. 表单元素忘记写name属性,后端获取参数为null

  8. 过滤器忘记配置urlPatterns,导致过滤器不生效

  9. 忽略编码设置,前端传递中文参数后端乱码

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐