“如果说Tomcat是Java Web世界的’执法机构’,那么Servlet规范就是这片天地的’根本大法’。它不关心你用什么框架,只定义了一套所有Web容器都必须遵守的行为准则。”
在Java Web的世界里,一切组件的生命周期都由Servlet接口严格定义。这不是一个简单的API,而是一套精妙的契约。
// Servlet接口定义了Web组件的完整生命周期
public interface Servlet {
// 诞生时刻 - 容器在Servlet实例化后立即调用
void init(ServletConfig config) throws ServletException;
// 服务时刻 - 处理请求的核心方法
void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
// 消亡时刻 - 容器在卸载Servlet前调用
void destroy();
// 身份信息
ServletConfig getServletConfig();
String getServletInfo();
}
深入解析生命周期:
public class LifecycleServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(LifecycleServlet.class);
private List<String> requestHistory = Collections.synchronizedList(new ArrayList<>());
@Override
public void init() throws ServletException {
// 注意:这是无参的init(),由GenericServlet提供
logger.info("🚀 Servlet诞生了!线程: {}", Thread.currentThread().getName());
// 模拟初始化数据库连接、加载配置等一次性操作
logger.info("📊 初始化数据库连接池...");
logger.info("🔧 加载系统配置...");
}
@Override
public void init(ServletConfig config) throws ServletException {
// 先调用父类的init(config),它会调用无参的init()
super.init(config);
logger.info("⚙️ Servlet配置信息: {}", config.getServletName());
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String requestInfo = String.format("%s %s from %s",
req.getMethod(), req.getRequestURI(), req.getRemoteAddr());
requestHistory.add(requestInfo);
logger.info("🎯 处理请求: {} (历史请求数: {})",
requestInfo, requestHistory.size());
// 调用父类的service方法,它会根据请求方法路由到doGet/doPost等
super.service(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head><title>Servlet生命周期</title></head>");
out.println("<body>");
out.println("<h1>Servlet生命周期演示</h1>");
out.println("<p>当前时间: " + new Date() + "</p>");
out.println("<p>请求历史数量: " + requestHistory.size() + "</p>");
out.println("<ul>");
for (String history : requestHistory) {
out.println("<li>" + history + "</li>");
}
out.println("</ul>");
out.println("</body>");
out.println("</html>");
}
@Override
public void destroy() {
logger.info("💀 Servlet即将消亡...");
logger.info("📈 统计信息: 总共处理了 {} 个请求", requestHistory.size());
// 模拟清理资源
requestHistory.clear();
logger.info("🧹 资源清理完成");
}
}
生命周期的重要观察:
单例模式:每个Servlet类在容器中只有一个实例多线程环境:同一个Servlet实例同时服务多个请求线程安全挑战:实例变量需要同步控制初始化时机:可以通过
<load-on-startup>控制
这两个对象看似简单,实则是容器为我们封装的强大工具。理解它们的本质,才能真正理解Web编程。
public class RequestInspectionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html><head><title>请求解剖</title></head><body>");
out.println("<h1>HTTP请求的完整解剖</h1>");
// 1. 请求行信息
out.println("<h2>请求行</h2>");
out.println("<p>方法: " + request.getMethod() + "</p>");
out.println("<p>URL: " + request.getRequestURL() + "</p>");
out.println("<p>URI: " + request.getRequestURI() + "</p>");
out.println("<p>协议: " + request.getProtocol() + "</p>");
// 2. 请求头信息
out.println("<h2>请求头</h2>");
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
out.println("<p><b>" + name + "</b>: " + value + "</p>");
}
// 3. 参数信息
out.println("<h2>请求参数</h2>");
Map<String, String[]> params = request.getParameterMap();
for (Map.Entry<String, String[]> entry : params.entrySet()) {
out.println("<p><b>" + entry.getKey() + "</b>: " +
String.join(", ", entry.getValue()) + "</p>");
}
// 4. 属性信息(请求域)
out.println("<h2>请求属性</h2>");
Enumeration<String> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String name = attrNames.nextElement();
Object value = request.getAttribute(name);
out.println("<p><b>" + name + "</b>: " + value + "</p>");
}
out.println("</body></html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 演示请求体的读取
StringBuilder body = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
body.append(line);
}
}
System.out.println("POST请求体: " + body.toString());
doGet(request, response);
}
}
深入理解Request/Response的设计:
门面模式(Facade Pattern):
// 实际的HttpServletRequest可能包含多个底层对象
public class HttpServletRequestFacade implements HttpServletRequest {
private HttpServletRequest request;
public HttpServletRequestFacade(HttpServletRequest request) {
this.request = request;
}
// 所有方法都委托给真实的request对象
public String getMethod() {
return request.getMethod();
}
// ... 其他方法
}
装饰器模式(Decorator Pattern):
// 可以对Request进行装饰,添加功能
public class LoggingHttpServletRequest extends HttpServletRequestWrapper {
public LoggingHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
System.out.println("获取参数: " + name + " = " + value);
return value;
}
}
Filter是Servlet规范中责任链模式的经典实现,它允许我们在请求到达Servlet之前和响应返回客户端之后插入处理逻辑。
// 一个完整的Filter链演示
@WebFilter("/*")
public class FilterChainDemo implements Filter {
private static final Logger logger = LoggerFactory.getLogger(FilterChainDemo.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("🔧 Filter初始化: {}", filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
logger.info("🛡️ Filter开始处理: {} {}",
httpRequest.getMethod(), httpRequest.getRequestURI());
// 1. 预处理 - 在chain.doFilter之前
httpRequest.setAttribute("startTime", startTime);
// 设置通用响应头
httpResponse.setHeader("X-Powered-By", "Java-Servlet");
// 2. 将请求传递给下一个Filter或目标Servlet
chain.doFilter(request, response);
// 3. 后处理 - 在chain.doFilter之后
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
logger.info("⏱️ 请求处理完成: {} {} - 耗时: {}ms",
httpRequest.getMethod(), httpRequest.getRequestURI(), duration);
// 添加处理时间到响应头
httpResponse.setHeader("X-Processing-Time", duration + "ms");
}
@Override
public void destroy() {
logger.info("🧹 Filter销毁");
}
}
多个Filter组成的责任链:
// 认证Filter
@WebFilter("/*")
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute("user") == null) {
// 未登录,重定向到登录页面
((HttpServletResponse) response).sendRedirect("/login");
return;
}
logger.info("✅ 用户已认证: {}", session.getAttribute("user"));
chain.doFilter(request, response);
}
}
// 日志Filter
@WebFilter("/*")
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 请求前日志
logRequest((HttpServletRequest) request);
chain.doFilter(request, response);
// 响应后日志
logResponse((HttpServletRequest) request, (HttpServletResponse) response);
}
private void logRequest(HttpServletRequest request) {
// 记录请求信息
}
private void logResponse(HttpServletRequest request, HttpServletResponse response) {
// 记录响应信息
}
}
// 编码Filter
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
chain.doFilter(request, response);
}
}
Filter执行顺序:
请求 → EncodingFilter → AuthenticationFilter → LoggingFilter → Target Servlet
Listener允许我们监听Web应用中的各种事件,是观察者模式在Servlet规范中的完美体现。
// 应用生命周期监听器
@WebListener
public class ApplicationLifecycleListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
System.out.println("🎉 Web应用启动: " + context.getContextPath());
// 应用启动时的初始化工作
initializeDatabasePool(context);
loadConfiguration(context);
scheduleBackgroundTasks(context);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
System.out.println("🛑 Web应用关闭: " + context.getContextPath());
// 应用关闭时的清理工作
shutdownDatabasePool(context);
cancelBackgroundTasks(context);
}
private void initializeDatabasePool(ServletContext context) {
// 初始化数据库连接池
System.out.println("📊 初始化数据库连接池...");
context.setAttribute("dbPool", "模拟的连接池");
}
private void loadConfiguration(ServletContext context) {
// 加载配置文件
System.out.println("⚙️ 加载应用配置...");
}
private void scheduleBackgroundTasks(ServletContext context) {
// 调度后台任务
System.out.println("🕒 启动后台任务调度...");
}
private void shutdownDatabasePool(ServletContext context) {
// 关闭数据库连接池
System.out.println("📊 关闭数据库连接池...");
}
private void cancelBackgroundTasks(ServletContext context) {
// 取消后台任务
System.out.println("🕒 停止后台任务调度...");
}
}
// Session生命周期监听器
@WebListener
public class SessionLifecycleListener implements HttpSessionListener {
private final AtomicInteger activeSessions = new AtomicInteger();
@Override
public void sessionCreated(HttpSessionEvent se) {
int count = activeSessions.incrementAndGet();
HttpSession session = se.getSession();
System.out.println("🆕 Session创建: " + session.getId());
System.out.println("👥 活跃Session数: " + count);
// 设置Session超时时间(秒)
session.setMaxInactiveInterval(30 * 60); // 30分钟
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
int count = activeSessions.decrementAndGet();
HttpSession session = se.getSession();
System.out.println("🗑️ Session销毁: " + session.getId());
System.out.println("👥 剩余活跃Session数: " + count);
// 清理Session相关资源
cleanupSessionResources(session);
}
private void cleanupSessionResources(HttpSession session) {
// 清理Session相关的临时文件、缓存等
System.out.println("🧹 清理Session资源: " + session.getId());
}
}
// 请求生命周期监听器
@WebListener
public class RequestLifecycleListener implements ServletRequestListener {
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
@Override
public void requestInitialized(ServletRequestEvent sre) {
startTime.set(System.currentTimeMillis());
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
System.out.println("📨 请求到达: " + request.getMethod() + " " + request.getRequestURI());
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
Long start = startTime.get();
if (start != null) {
long duration = System.currentTimeMillis() - start;
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
System.out.println("📤 请求完成: " + request.getMethod() + " " +
request.getRequestURI() + " - 耗时: " + duration + "ms");
}
startTime.remove();
}
}
Session机制是Web开发中状态管理的核心,理解它的工作原理至关重要。
@WebServlet("/session-demo")
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();
out.println("<html>");
out.println("<head><title>Session深度探索</title></head>");
out.println("<body>");
out.println("<h1>Session机制深度解析</h1>");
// Session基本信息
out.println("<h2>Session基本信息</h2>");
out.println("<p>Session ID: " + session.getId() + "</p>");
out.println("<p>创建时间: " + new Date(session.getCreationTime()) + "</p>");
out.println("<p>最后访问: " + new Date(session.getLastAccessedTime()) + "</p>");
out.println("<p>最大空闲时间: " + session.getMaxInactiveInterval() + "秒</p>");
out.println("<p>是否新Session: " + session.isNew() + "</p>");
// Session属性操作
Integer visitCount = (Integer) session.getAttribute("visitCount");
if (visitCount == null) {
visitCount = 1;
} else {
visitCount++;
}
session.setAttribute("visitCount", visitCount);
out.println("<p>访问次数: " + visitCount + "</p>");
// 显示所有Session属性
out.println("<h2>Session中的所有属性</h2>");
Enumeration<String> attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
Object value = session.getAttribute(name);
out.println("<p><b>" + name + "</b>: " + value + "</p>");
}
// Session操作按钮
out.println("<h2>Session操作</h2>");
out.println("<form method='post'>");
out.println("<button type='submit' name='action' value='invalidate'>使Session失效</button>");
out.println("<button type='submit' name='action' value='addAttribute'>添加属性</button>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
HttpSession session = request.getSession();
if ("invalidate".equals(action)) {
session.invalidate();
System.out.println("Session已被手动失效: " + session.getId());
} else if ("addAttribute".equals(action)) {
String key = "key_" + System.currentTimeMillis();
String value = "value_" + (int)(Math.random() * 1000);
session.setAttribute(key, value);
System.out.println("添加Session属性: " + key + " = " + value);
}
response.sendRedirect(request.getRequestURI());
}
}
Session的工作原理深度解析:
// 模拟Session管理器的实现
public class SimpleSessionManager {
private final Map<String, HttpSession> sessions = new ConcurrentHashMap<>();
private final ScheduledExecutorService cleaner = Executors.newScheduledThreadPool(1);
public SimpleSessionManager() {
// 定时清理过期Session
cleaner.scheduleAtFixedRate(this::cleanExpiredSessions, 1, 1, TimeUnit.MINUTES);
}
public HttpSession getSession(String sessionId, boolean create) {
if (sessionId != null) {
HttpSession session = sessions.get(sessionId);
if (session != null && !session.isExpired()) {
session.access(); // 更新最后访问时间
return session;
}
}
if (create) {
return createSession();
}
return null;
}
private HttpSession createSession() {
String sessionId = generateSessionId();
HttpSession session = new SimpleHttpSession(sessionId);
sessions.put(sessionId, session);
return session;
}
private void cleanExpiredSessions() {
Iterator<Map.Entry<String, HttpSession>> it = sessions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, HttpSession> entry = it.next();
if (entry.getValue().isExpired()) {
it.remove();
System.out.println("清理过期Session: " + entry.getKey());
}
}
}
private String generateSessionId() {
return UUID.randomUUID().toString();
}
}
很多人认为JSP是独立的技术,实际上它只是Servlet的另一种表现形式。
JSP到Servlet的转换过程:
<%-- sample.jsp --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP示例</title>
</head>
<body>
<h1>欢迎来到JSP世界</h1>
<%-- 脚本片段 --%>
<%
String name = request.getParameter("name");
if (name == null) name = "游客";
%>
<%-- 表达式 --%>
<p>你好, <%= name %>!</p>
<%-- 声明 --%>
<%!
private int visitCount = 0;
public String getWelcomeMessage() {
return "这是第" + (++visitCount) + "次访问";
}
%>
<p><%= getWelcomeMessage() %></p>
<%-- 指令 --%>
<%@ include file="footer.jsp" %>
</body>
</html>
转换后的Servlet代码(简化版):
public class sample_jsp extends HttpServlet {
private int visitCount = 0;
public String getWelcomeMessage() {
return "这是第" + (++visitCount) + "次访问";
}
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// JSP转换后的代码
out.write("<html>");
out.write("<head><title>JSP示例</title></head>");
out.write("<body>");
out.write("<h1>欢迎来到JSP世界</h1>");
// 脚本片段转换
String name = request.getParameter("name");
if (name == null) name = "游客";
// 表达式转换
out.write("<p>你好, ");
out.print(name);
out.write("!</p>");
// 声明的方法调用
out.write("<p>");
out.print(getWelcomeMessage());
out.write("</p>");
// include指令转换
out.write("<!-- footer内容 -->");
out.write("</body>");
out.write("</html>");
}
}
本章我们深入探索了Servlet规范这个Java Web世界的"宪法"。从Servlet的生命周期,到Request/Response的本质,再到Filter、Listener的设计模式应用,我们看到了一个完整而优雅的设计体系。
关键洞察:
契约精神:Servlet规范定义了容器和组件之间的明确契约设计模式:Filter的责任链、Listener的观察者模式体现了优秀的设计生命周期管理:明确的生命周期让资源管理变得可控状态管理:Session机制巧妙地解决了HTTP无状态的问题“理解Servlet规范,就像理解交通规则。无论你开什么车(使用什么框架),都要遵守同样的交通规则。这保证了整个生态的互操作性和一致性。”
思考题1:为什么Servlet是单例的?如果我们需要在Servlet中保存用户特定的数据,应该怎么做?
答案:
为什么采用单例模式:
性能优化:避免频繁创建和销毁对象的开销资源节约:大量并发请求时,内存占用保持稳定设计哲学:Servlet应该专注于业务逻辑,而不是状态管理正确保存用户特定数据的方法:
// ❌ 错误方式:使用实例变量(线程不安全)
public class UnsafeServlet extends HttpServlet {
private String currentUser; // 多个用户会相互覆盖
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
currentUser = req.getParameter("user"); // 竞态条件!
// ... 使用currentUser
}
}
// ✅ 正确方式1:使用局部变量(线程安全)
public class SafeServlet1 extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String currentUser = req.getParameter("user"); // 局部变量,线程安全
// ... 使用currentUser
}
}
// ✅ 正确方式2:使用Session(用户级别状态)
public class SafeServlet2 extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
HttpSession session = req.getSession();
session.setAttribute("user", req.getParameter("user"));
String currentUser = (String) session.getAttribute("user");
// ... 使用currentUser
}
}
// ✅ 正确方式3:使用ThreadLocal(请求级别状态)
public class SafeServlet3 extends HttpServlet {
private static final ThreadLocal<String> currentUser = new ThreadLocal<>();
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
currentUser.set(req.getParameter("user"));
// ... 使用currentUser.get()
} finally {
currentUser.remove(); // 重要:清理ThreadLocal,避免内存泄漏
}
}
}
思考题2:Filter链中,如果某个Filter没有调用FilterChain.doFilter()方法,会发生什么?
答案:
如果Filter没有调用
chain.doFilter(),请求处理链会在该Filter处中断,后续的Filter和目标Servlet都不会被执行。
这种机制的实际应用场景:
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 检查用户是否登录
if (!isAuthenticated(httpRequest)) {
// 未认证,中断Filter链,直接返回401错误
httpResponse.sendError(401, "需要身份认证");
return; // 注意:这里没有调用chain.doFilter()
}
// 已认证,继续执行后续Filter和Servlet
chain.doFilter(request, response);
}
private boolean isAuthenticated(HttpServletRequest request) {
HttpSession session = request.getSession(false);
return session != null && session.getAttribute("user") != null;
}
}
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("请求到达: " + ((HttpServletRequest) request).getRequestURI());
chain.doFilter(request, response);
System.out.println("请求完成: " + ((HttpServletRequest) request).getRequestURI());
}
}
执行流程:
请求 → AuthenticationFilter → [如果未认证] → 返回401,流程结束
↓ [如果已认证]
→ LoggingFilter → Target Servlet
思考题3:Session是如何在集群环境中实现的?
答案:
在单机环境中,Session存储在Web容器的内存中。但在集群环境中,需要解决Session共享问题。
集群Session的解决方案:
Session粘滞(Sticky Session)
// 负载均衡器通过Cookie或URL重写将同一用户的请求总是路由到同一台服务器
// 优点:实现简单,性能好
// 缺点:缺乏容错性,服务器宕机导致Session丢失
Session复制(Session Replication)
// 所有服务器间同步Session数据
// 配置示例(Tomcat):
// server.xml:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
// web.xml:
<distributable/>
// 优点:容错性好
// 缺点:网络开销大,扩展性差
集中式Session存储(推荐)
// 使用外部存储如Redis、数据库等集中管理Session
public class RedisSessionManager {
private JedisPool jedisPool;
public void setAttribute(String sessionId, String key, Object value) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.hset("session:" + sessionId, key, serialize(value));
jedis.expire("session:" + sessionId, 1800); // 30分钟过期
}
}
public Object getAttribute(String sessionId, String key) {
try (Jedis jedis = jedisPool.getResource()) {
String data = jedis.hget("session:" + sessionId, key);
return deserialize(data);
}
}
}
// 通过Filter实现Session的统一管理
public class DistributedSessionFilter implements Filter {
private RedisSessionManager sessionManager;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 包装Request,提供分布式Session支持
DistributedSessionRequest wrappedRequest =
new DistributedSessionRequest(httpRequest, sessionManager);
chain.doFilter(wrappedRequest, response);
}
}
无状态设计(现代微服务架构推荐)
// 使用JWT等Token机制,完全避免服务器端Session
public class StatelessAuthentication {
public String createToken(User user) {
return JWT.create()
.withSubject(user.getId())
.withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
.sign(Algorithm.HMAC256("secret"));
}
public User validateToken(String token) {
// 验证Token并返回用户信息
// 不需要服务器端存储Session
}
}
最佳实践建议:
中小型集群:Session粘滞 + 简单的故障转移大型分布式系统:集中式Session存储(Redis)现代微服务:无状态设计 + JWT(下一章预告:我们将探索Spring框架如何基于Servlet规范构建,以及它如何通过控制反转(IoC)和面向切面编程(AOP)等特性,彻底改变Java Web开发的方式。)