1. Servlet技术深度解析从入门到实战作为一名有十年Java Web开发经验的老兵我见证了Servlet技术从JSP时代到Spring MVC框架的演进过程。今天我想分享的是Servlet技术中最核心的三个实战要点生命周期管理、创建方式选择和前后台参数传递技巧。这些知识看似基础但真正能吃透的开发者不足三成。Servlet作为Java EE体系中最基础的Web组件其重要性不言而喻。即使在Spring Boot大行其道的今天底层仍然是Servlet在支撑整个HTTP请求处理流程。理解Servlet的运作机制能帮助我们更好地排查线上问题、优化系统性能甚至自定义开发框架组件。2. Servlet生命周期全流程剖析2.1 生命周期阶段详解Servlet生命周期包含三个关键阶段初始化阶段当容器首次接收到针对该Servlet的请求时会调用init()方法。这个方法在整个生命周期中只会执行一次通常用于加载静态资源或建立数据库连接。public void init(ServletConfig config) throws ServletException { // 初始化数据库连接池 this.dataSource initDataSource(); logger.info(Servlet初始化完成); }服务阶段对于每个HTTP请求容器会创建一个新线程并调用service()方法。根据请求类型不同会进一步调用doGet()或doPost()等方法。销毁阶段当容器决定卸载Servlet时通常是在应用关闭时会调用destroy()方法释放资源。重要提示init()和destroy()方法在Servlet生命周期中只会执行一次而service()方法会针对每个请求执行。这种设计决定了我们应该在不同阶段做合适的事情。2.2 生命周期中的线程安全问题由于Servlet是单例的所有请求共享同一个实例这就带来了线程安全问题。以下是一个典型错误示例public class UnsafeServlet extends HttpServlet { private int counter; // 实例变量存在线程安全问题 protected void doGet(HttpServletRequest req, HttpServletResponse resp) { counter; // ... } }解决方案有两种使用局部变量替代实例变量对共享资源加锁但会影响性能2.3 生命周期回调的实用技巧通过实现ServletContextListener接口我们可以监听Servlet生命周期的关键事件public class AppLifecycleListener implements ServletContextListener { Override public void contextInitialized(ServletContextEvent sce) { // 应用启动时执行 } Override public void contextDestroyed(ServletContextEvent sce) { // 应用关闭时执行 } }在web.xml中配置listener listener-classcom.example.AppLifecycleListener/listener-class /listener3. Servlet的三种创建方式对比3.1 实现Servlet接口传统方式这是最基础的方式需要实现所有方法public class BasicServlet implements Servlet { private ServletConfig config; public void init(ServletConfig config) { this.config config; } public void service(ServletRequest req, ServletResponse res) { // 处理请求 } // 其他必要方法... }优点灵活性高 缺点需要实现所有方法代码冗余3.2 继承GenericServlet适配器模式GenericServlet提供了大部分方法的默认实现public class BetterServlet extends GenericServlet { public void service(ServletRequest req, ServletResponse res) { // 只需实现核心业务逻辑 } }优点减少了模板代码 缺点仍需处理ServletRequest/ServletResponse的原始类型3.3 继承HttpServlet推荐方式这是最常用的方式专门针对HTTP协议进行了优化public class UserServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) { // 处理GET请求 } protected void doPost(HttpServletRequest req, HttpServletResponse resp) { // 处理POST请求 } }优点自动区分HTTP方法提供类型安全的请求/响应对象内置常用功能如lastModified支持3.4 创建方式选型建议对于新项目建议直接使用HttpServlet。只有在需要处理非HTTP协议时才考虑GenericServlet或原始Servlet接口。4. 前后台参数传递的六种实战方案4.1 URL查询参数传递前端通过URL传递参数http://example.com/user?name张三age25后端获取方式String name request.getParameter(name); String age request.getParameter(age);注意事项URL参数有长度限制通常2048字符且参数值需要URL编码4.2 表单POST提交前端表单form action/register methodpost input nameusername input namepassword typepassword /form后端处理protected void doPost(HttpServletRequest req, HttpServletResponse resp) { String username req.getParameter(username); String password req.getParameter(password); }4.3 JSON数据交互RESTful风格前端发送JSONfetch(/api/user, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({name: 张三, age: 25}) });后端解析protected void doPost(HttpServletRequest req, HttpServletResponse resp) { BufferedReader reader req.getReader(); StringBuilder sb new StringBuilder(); String line; while ((line reader.readLine()) ! null) { sb.append(line); } JSONObject json new JSONObject(sb.toString()); String name json.getString(name); int age json.getInt(age); }4.4 文件上传处理前端表单form action/upload methodpost enctypemultipart/form-data input typefile namefile /form后端处理需要MultipartConfig注解MultipartConfig public class UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) { Part filePart req.getPart(file); String fileName filePart.getSubmittedFileName(); filePart.write(/uploads/ fileName); } }4.5 Cookie的使用设置CookieCookie cookie new Cookie(user, zhangsan); cookie.setMaxAge(3600); // 1小时有效期 cookie.setPath(/); response.addCookie(cookie);读取CookieCookie[] cookies request.getCookies(); for (Cookie cookie : cookies) { if (user.equals(cookie.getName())) { String value cookie.getValue(); break; } }4.6 Session管理设置Session属性HttpSession session request.getSession(); session.setAttribute(currentUser, user);获取Session属性User user (User) request.getSession().getAttribute(currentUser);性能提示Session默认使用内存存储在集群环境下需要考虑分布式Session方案5. 高级技巧与性能优化5.1 异步Servlet处理传统Servlet会阻塞线程直到处理完成而异步Servlet可以释放线程WebServlet(urlPatterns /async, asyncSupported true) public class AsyncServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) { AsyncContext context req.startAsync(); CompletableFuture.runAsync(() - { // 耗时操作 context.getResponse().getWriter().write(Done); context.complete(); }); } }5.2 过滤器(Filter)的应用过滤器可以在请求到达Servlet前进行预处理public class LogFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { long start System.currentTimeMillis(); chain.doFilter(req, res); // 继续执行过滤器链 long time System.currentTimeMillis() - start; logger.info(请求处理时间: {}ms, time); } }5.3 性能优化建议合理使用Servlet缓存对静态内容设置缓存头response.setHeader(Cache-Control, max-age3600);避免在Servlet中创建大量对象考虑使用对象池合理配置线程池在web.xml中调整容器线程池大小及时释放资源在finally块中关闭数据库连接等资源6. 常见问题排查指南6.1 中文乱码问题解决方案// 对于GET请求 String name new String(request.getParameter(name).getBytes(ISO-8859-1), UTF-8); // 对于POST请求 request.setCharacterEncoding(UTF-8);6.2 404错误排查步骤检查web.xml中的url-pattern配置确认Servlet类有WebServlet注解或web.xml配置检查上下文路径是否正确6.3 500错误常见原因空指针异常未检查参数是否为null类转换异常从Session获取对象时强制转换失败数据库连接失败6.4 内存泄漏预防常见泄漏点静态集合持续增长未关闭的IO流线程未正确终止7. Servlet在现代架构中的定位虽然现在流行各种Web框架但Servlet仍然是Java Web技术的基石。理解Servlet原理有助于深度优化Spring MVC应用开发高性能网关组件实现自定义过滤器理解Web容器工作原理我在实际项目中就曾通过重写Servlet的service方法实现了API调用频率限制功能。这种底层控制能力是框架无法提供的。