Web 基础
理解 Spring 之前必须搞清的 Web 底层知识。Servlet 是 Java Web 的基石,Spring MVC 本质上是对 Servlet 的封装。
概念
- Web 应用的请求处理依赖 Servlet 容器(如 Tomcat),容器负责管理 Servlet 的生命周期和请求分发。
- Filter 和 Listener 是 Servlet 规范提供的扩展机制,Spring Security 的过滤器链就建立在 Filter 之上。
- HTTP 是无状态协议,Cookie、Session、Token 是三种维持用户状态的方案。
Servlet 生命周期
Servlet 容器(Tomcat)管理 Servlet 的完整生命周期:
| 阶段 | 方法 | 说明 |
|---|---|---|
| 加载与实例化 | 构造方法 | 容器启动或首次请求时创建 Servlet 实例(单例) |
| 初始化 | init(ServletConfig) | 只调用一次,读取配置、初始化资源 |
| 处理请求 | service(request, response) | 每次请求调用,根据 HTTP 方法分派到 doGet/doPost 等 |
| 销毁 | destroy() | 容器关闭时调用,释放资源 |
关键点: Servlet 是单例的,多个请求共享同一个实例,因此 Servlet 中不应有可变的实例变量(线程安全问题)。
Servlet 容器工作原理
Tomcat 处理一个请求的流程:
- 监听端口,接收 TCP 连接
- 解析 HTTP 请求,创建
HttpServletRequest和HttpServletResponse对象 - 根据 URL 匹配对应的 Servlet(通过 web.xml 或注解
@WebServlet) - 调用 Filter 链(如果有)
- 调用 Servlet 的
service()方法 - Servlet 写入响应内容
- 容器将响应发送给客户端
Filter 与 Listener
Filter 链机制
Filter 在请求到达 Servlet 之前和响应返回客户端之前执行,形成链式调用:
客户端 → Filter1 → Filter2 → Filter3 → Servlet → Filter3 → Filter2 → Filter1 → 客户端典型用途:编码设置、日志记录、权限校验、跨域处理。
Filter vs Spring Interceptor
| 对比项 | Filter | Interceptor |
|---|---|---|
| 规范 | Servlet 规范 | Spring MVC 框架 |
| 作用范围 | 所有请求(包括静态资源) | 只作用于 DispatcherServlet 处理的请求 |
| 执行时机 | 在 Servlet 之前 | 在 Handler(Controller)前后 |
| 访问 Spring 容器 | 不能直接注入 Bean | 可以访问 Spring 容器 |
| 典型场景 | 编码、安全过滤 | 登录校验、日志、权限 |
Listener
监听 Servlet 容器中的事件:
ServletContextListener:应用启动和关闭时触发,常用于初始化全局资源HttpSessionListener:Session 创建和销毁时触发,可用于在线用户统计ServletRequestListener:请求创建和销毁时触发
HTTP 请求响应要点
常用 HTTP 方法
| 方法 | 语义 | 幂等 | 安全 |
|---|---|---|---|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 / 提交数据 | 否 | 否 |
| PUT | 全量替换 | 是 | 否 |
| DELETE | 删除资源 | 是 | 否 |
状态码分类
| 范围 | 含义 | 常见 |
|---|---|---|
| 1xx | 信息性 | 100 Continue |
| 2xx | 成功 | 200 OK、201 Created、204 No Content |
| 3xx | 重定向 | 301 永久、302 临时、304 未修改 |
| 4xx | 客户端错误 | 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found |
| 5xx | 服务器错误 | 500 Internal Server Error、502 Bad Gateway、503 Service Unavailable |
常用 Header
| Header | 说明 |
|---|---|
| Content-Type | 请求/响应体的 MIME 类型(application/json、text/html) |
| Authorization | 认证凭证(Bearer Token) |
| Cache-Control | 缓存策略 |
| Set-Cookie / Cookie | 服务端设置 / 客户端携带 Cookie |
| X-Forwarded-For | 经过代理时的真实客户端 IP |
Cookie vs Session vs Token
| 对比项 | Cookie | Session | Token (JWT) |
|---|---|---|---|
| 存储位置 | 客户端浏览器 | 服务端内存/Redis | 客户端(localStorage/Cookie) |
| 安全性 | 较低(可被篡改) | 较高(数据在服务端) | 中等(签名防篡改,但不加密) |
| 跨域 | 受同源策略限制 | 依赖 Cookie 传递 Session ID | 天然支持跨域(Header 携带) |
| 服务端压力 | 无 | 需存储所有用户状态 | 无(无状态) |
| 水平扩展 | 支持 | 需要共享 Session(Redis) | 天然支持(任何节点都能验证) |
| 主动失效 | 设置过期时间 | 服务端销毁 | 无法主动失效(需配合黑名单) |
为什么现在更倾向 Token?
- 前后端分离架构下,前端可能是 Web、App、小程序,Token 方案统一且不依赖 Cookie
- 微服务架构下,Session 共享复杂,Token 天然无状态,适合水平扩展
- 但 Token 也有缺点:无法主动撤销、payload 不宜存敏感数据、刷新机制需要额外设计
为什么 Spring 要封装 Servlet
原生 Servlet 开发的痛点:
- 模板代码多 — 每个 Servlet 都要继承 HttpServlet、重写 doGet/doPost
- 参数解析繁琐 — 手动
request.getParameter(),类型转换、校验全靠自己 - 缺少 IoC 支持 — 对象依赖关系手动管理,无法注入
- 路由分散 — URL 映射在 web.xml 或注解中,缺乏统一管理
- 异常处理困难 — 没有全局异常处理机制
Spring MVC 通过 DispatcherServlet 统一入口 + 注解驱动解决了这些问题。
面试常问 & 怎么答
Q1: Servlet 的生命周期?
Servlet 是单例的,容器启动或首次请求时创建实例,调用 init() 初始化;每次请求调用 service() 方法(分派到 doGet/doPost);容器关闭时调用 destroy() 销毁。因为是单例,要注意线程安全。
Q2: Filter 和 Interceptor 的区别?
Filter 是 Servlet 规范的,作用于所有请求(包括静态资源),在 Servlet 之前执行;Interceptor 是 Spring MVC 的,只作用于 Controller 请求,可以访问 Spring 容器。执行顺序是 Filter → DispatcherServlet → Interceptor → Controller。
Q3: Cookie、Session、Token 各自的优缺点?
Cookie 存客户端,容量小且受同源策略限制;Session 存服务端,安全但不利于水平扩展(需 Redis 共享);Token(JWT)无状态,天然支持分布式和跨域,但无法主动撤销。现在前后端分离 + 微服务架构下更倾向 Token。
Q4: 为什么现在更倾向用 JWT 而不是 Session?
两个原因:一是前后端分离后前端形态多样(Web、App、小程序),JWT 通过 Header 携带,不依赖 Cookie;二是微服务架构下 Session 共享需要额外基础设施(Redis),JWT 无状态,任何节点都能验证。
看到什么就先想到这类
- 出现 Servlet、Filter、Listener、web.xml。
- 出现 Cookie、Session、Token、JWT 的对比。
- 出现 HTTP 方法、状态码、Header 基础问题。
- 出现"Spring 为什么要封装 / Spring MVC 和 Servlet 的关系"。