Servlet 教程(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
什么是 Servlet?为什么需要它?
Servlet 是 Java 语言中用于开发 Web 应用程序的核心技术之一。它可以理解为运行在服务器端的 Java 小程序,负责处理客户端(如浏览器)发送的 HTTP 请求,并生成相应的 HTTP 响应。
想象一个餐厅场景:当顾客(浏览器)发送点餐请求时,服务员(Servlet)会接收订单,处理后将餐品(响应内容)返回给顾客。Servlet 的核心作用,就是充当这个“服务员”,在 Web 应用中实现请求与响应的交互逻辑。
对于开发人员来说,Servlet 提供了以下优势:
- 平台无关性:基于 Java 的跨平台特性,Servlet 可以在任何支持 Java 的服务器上运行。
- 高效性:Servlet 运行在服务器内存中,生命周期由容器管理,避免了传统 CGI 程序重复加载解释器的性能损耗。
- 扩展性:通过继承或接口实现,可以灵活定制请求处理逻辑。
开发环境搭建
必备工具清单
工具名称 | 作用描述 | 版本建议 |
---|---|---|
Java JDK | Servlet 运行的基础环境 | 1.8 或更高版本 |
Apache Tomcat | Servlet 容器,负责部署和运行 | 9.0 或更高版本 |
IDE(如 IntelliJ IDEA) | 提供代码编写和调试支持 | 最新稳定版 |
环境配置步骤
-
安装 Java JDK
下载并配置环境变量,确保javac
和java
命令可用。javac -version # 验证 JDK 安装成功
-
下载并启动 Tomcat
从 Apache 官网 下载 Tomcat,解压后执行:bin/startup.sh # Linux/Mac 系统 bin/startup.bat # Windows 系统
访问
http://localhost:8080
,若显示 Tomcat 欢迎页,说明环境正常。 -
配置 IDE
在 IntelliJ IDEA 中创建 Maven 项目,添加以下依赖到pom.xml
:<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>
Servlet 的核心概念与生命周期
核心接口与类
Servlet 的功能主要通过以下接口实现:
Servlet
接口:定义了 Servlet 的基本方法(init()
,service()
,destroy()
)。GenericServlet
抽象类:实现了Servlet
接口,提供通用功能,适用于非 HTTP 协议场景。HttpServlet
抽象类:继承自GenericServlet
,专为 HTTP 协议设计,提供了doGet()
,doPost()
等方法。
生命周期阶段
Servlet 的生命周期由容器(如 Tomcat)管理,分为三个阶段:
-
初始化阶段
容器调用init()
方法,完成资源加载(如数据库连接池、配置文件读取)。 -
服务阶段
容器调用service()
方法,根据请求类型(GET/POST)分发到对应的doGet()
或doPost()
方法。 -
销毁阶段
容器调用destroy()
方法,释放资源(如关闭数据库连接)。
生命周期的比喻:
将 Servlet 比作一个餐厅服务员,init()
是服务员上岗培训,service()
是接待顾客处理订单,destroy()
是下班前整理工作台。
请求与响应对象
Servlet 处理请求的核心是 HttpServletRequest
和 HttpServletResponse
对象:
HttpServletRequest
:封装了客户端请求的详细信息,包括请求头、请求参数、会话信息等。HttpServletResponse
:用于构建响应内容,设置响应头、状态码、输出流等。
示例代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应编码为 UTF-8
response.setCharacterEncoding("UTF-8");
// 获取请求参数
String name = request.getParameter("name");
// 输出响应内容
response.getWriter().write("Hello, " + name + "!");
}
第一个 Servlet 程序:Hello World
代码实现
创建一个继承 HttpServlet
的类:
@WebServlet("/hello") // 注解配置 URL 映射
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("Hello Servlet World!");
}
}
部署与验证
- 将项目打包为
war
文件,放置到 Tomcat 的webapps
目录。 - 访问
http://localhost:8080/your-project-name/hello
,若显示“Hello Servlet World!”,则部署成功。
处理表单提交与数据验证
处理 POST 请求
当用户提交表单时,Servlet 需要通过 doPost()
方法接收参数:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// 验证逻辑
if (username.equals("admin") && password.equals("123456")) {
response.getWriter().write("登录成功");
} else {
response.sendRedirect("/login-failed.html"); // 重定向到失败页面
}
}
数据验证技巧
- 非空验证:检查参数是否为
null
或空字符串。 - 类型转换:使用
Integer.parseInt()
或Double.parseDouble()
时,需捕获NumberFormatException
。 - 正则表达式:验证邮箱格式、手机号等。
// 验证邮箱格式
if (!email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")) {
// 处理格式错误
}
使用会话与 Cookie
HttpSession 的基本用法
Servlet 提供 HttpSession
对象来维护用户会话状态:
// 获取当前会话,若不存在则创建新会话
HttpSession session = request.getSession();
// 存储用户信息
session.setAttribute("user", user);
// 移除属性
session.removeAttribute("user");
// 销毁会话
session.invalidate();
Cookie 的读写操作
通过 Cookie
类可以操作客户端的 Cookie:
// 写入 Cookie
Cookie cookie = new Cookie("theme", "dark");
cookie.setMaxAge(60 * 60 * 24); // 1 天后过期
response.addCookie(cookie);
// 读取 Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("theme".equals(cookie.getName())) {
String theme = cookie.getValue();
// 处理主题逻辑
}
}
}
异常处理与日志记录
异常处理机制
Servlet 可通过 @WebServlet
注解的 loadOnStartup
属性控制初始化顺序,或通过 Filter
统一处理异常:
// 自定义异常处理器
public class ErrorHandler extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
resp.getWriter().write("系统发生错误: " + throwable.getMessage());
}
}
日志记录实践
推荐使用 SLF4J 或 Logback 进行日志记录:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggerDemoServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(LoggerDemoServlet.class);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
logger.info("请求开始处理");
try {
// 业务逻辑
} catch (Exception e) {
logger.error("处理请求时发生错误", e);
throw e;
} finally {
logger.info("请求处理完成");
}
}
}
高级主题与最佳实践
异步处理与非阻塞 I/O
Servlet 3.0 引入了异步处理能力,允许在独立线程中执行耗时操作:
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
AsyncContext asyncContext = req.startAsync();
new Thread(() -> {
try {
// 模拟耗时操作
Thread.sleep(2000);
// 构建响应
PrintWriter writer = asyncContext.getResponse().getWriter();
writer.write("异步处理完成");
} catch (Exception e) {
asyncContext.complete();
} finally {
asyncContext.complete();
}
}).start();
}
}
安全性与过滤器
通过 Filter
实现跨站脚本(XSS)防护:
@WebFilter("/*")
public class XSSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 清洗请求参数
String cleanedParam = cleanXSS(httpRequest.getParameter("input"));
// 继续请求链
chain.doFilter(request, response);
}
private String cleanXSS(String value) {
// 简单的 XSS 清洗逻辑
return value.replaceAll("<", "<").replaceAll(">", ">");
}
}
性能优化建议
- 避免在
service()
方法中创建对象:频繁的new
操作会增加 GC 压力。 - 复用数据库连接:通过连接池(如 HikariCP)管理资源。
- 使用缓存:对频繁查询的数据使用内存缓存(如 Redis)。
常见问题与解决方案
问题 1:Servlet 无法被 Tomcat 加载
可能原因:
- 类路径错误,未在
web.xml
或@WebServlet
中配置 URL。 - Tomcat 未重启,导致新类未被检测到。
解决方案:
- 检查注解或
web.xml
中的配置:<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.example.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
问题 2:中文乱码问题
原因:请求或响应的编码未正确设置。
解决方案:
在 doGet()
或 doPost()
方法开头添加编码设置:
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
问题 3:会话超时未处理
解决方案:
在 web.xml
中配置全局会话超时时间:
<session-config>
<session-timeout>30</session-timeout> <!-- 30 分钟超时 -->
</session-config>
总结与展望
通过本文的学习,读者已经掌握了从基础概念到高级应用的 Servlet 技术全貌。从简单的 Hello World 到复杂的异步处理,Servlet 的灵活性和扩展性在 Web 开发中始终占据重要地位。
对于初学者,建议从简单项目入手,逐步实践表单处理、数据库交互等场景;中级开发者则可以深入探索异步编程、安全防护等进阶主题。随着云原生和微服务架构的普及,Servlet 的核心思想依然适用于现代后端开发,例如在 Spring Framework 中,Controller 组件底层仍然基于 Servlet 技术实现。
掌握 Servlet,不仅是学习 Java Web 的基础,更是理解 HTTP 协议和服务器端编程的必经之路。希望本文能成为您通往专业 Web 开发的坚实第一步。