跳转至

HttpServlet 踩坑问题

一、线程安全

HttpServlet 默认是单例,多线程共享。成员变量是非线程安全的:

public class MyServlet extends HttpServlet {
    private int count = 0;  // ❌ 所有请求共享
    protected void doGet(...) {
        count++;
    }
}

解决: - 不要用成员变量存请求状态。 - 用局部变量。 - 必须共享,用 AtomicInteger 或加锁。

二、响应已经提交

response.getWriter().write("hello");
// 业务出错
response.sendError(500);  // IllegalStateException: response already committed

响应一旦 flush / close,再改 status 就报错。

三、中文乱码

request.setCharacterEncoding("UTF-8");       // 必须在 getParameter 前
response.setContentType("text/html;charset=UTF-8");

GET 请求中文乱码在 Tomcat 配置 URIEncoding="UTF-8"

四、流只能读一次

request.getInputStream() 读完后,再 getParameter() 或再读流就读不到了。

解决:用 ContentCachingRequestWrapper(Spring)包装,或自定义 Filter 缓存 body。

五、异步处理

Servlet 3.0 支持异步:

@RequestMapping("/async")
public void asyncProcess() {
    AsyncContext ctx = request.startAsync();
    executor.execute(() -> {
        // 处理
        ctx.complete();
    });
}

避免 Tomcat 线程被阻塞。

六、Spring MVC 之后

现代开发很少直接写 HttpServlet,都用 @Controller / @RestController。但理解底层仍重要。

七、常见问题

  • 404:URL 映射错。
  • 405:方法不支持(GET/POST 不匹配)。
  • 500:业务异常。
  • 响应乱码:编码没设对。
  • 上传大小限制maxPostSize

面试加分

  • HttpServlet 单例多线程,成员变量线程不安全是高频考点。
  • 现代 Spring Boot 自动配置了大部分 Servlet 细节。