1. 什么是Servlet?从零开始理解Java Web核心组件
Servlet是Java EE规范中定义的一套接口,专门用于处理客户端(通常是浏览器)发送的HTTP请求并生成动态响应。简单来说,它就是运行在服务器端的Java程序,负责接收用户请求、处理业务逻辑,最后返回HTML页面或其他格式的响应数据。
我第一次接触Servlet是在2013年,当时为了理解这个看似简单的概念,整整花了两周时间调试各种404错误。现在回头看,Servlet其实就是Java Web开发的基石,所有现代框架(如Spring MVC)底层都是基于Servlet规范构建的。
重要提示:Servlet不是框架,而是一套标准接口。Tomcat等Web容器负责实现这些接口,开发者则编写具体的Servlet类来处理业务逻辑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境搭建与基础配置
2.1 必备工具清单
要开始Servlet开发,你需要准备以下环境:
- JDK 8或更高版本(推荐JDK 11 LTS)
- Apache Tomcat 9.x(最新稳定版)
- Maven 3.6+(项目管理工具)
- IntelliJ IDEA或Eclipse(带Java EE插件)
我强烈建议使用Maven管理项目依赖,而不是手动下载jar包。这能避免90%的类路径问题。
2.2 pom.xml关键配置详解
在Maven项目的pom.xml中,必须包含以下依赖:
xml复制<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSP支持 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
注意provided作用域非常重要,它表示这些依赖在运行时由Tomcat提供,不需要打包到WAR文件中。这是新手常犯的错误之一。
2.3 web.xml配置的现代替代方案
传统方式需要在web.xml中配置Servlet映射:
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>
但自从Servlet 3.0开始,推荐使用注解方式:
java复制@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
// 实现代码
}
3. 第一个Servlet程序实战
3.1 创建HttpServlet子类
所有自定义Servlet都应继承HttpServlet类,并重写关键方法:
java复制public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<html><body>");
out.println("<h1>Hello, Servlet World!</h1>");
out.println("</body></html>");
}
}
3.2 请求处理流程解析
当用户访问/hello时,Tomcat内部的处理流程是:
- 容器接收到HTTP请求
- 根据URL路径找到对应的Servlet
- 创建ServletRequest和ServletResponse对象
- 调用Servlet的service()方法
- service()方法根据请求类型(GET/POST等)调用对应的doXxx()方法
- 执行我们重写的业务逻辑
- 将响应返回给客户端
3.3 常见问题排查
问题1:访问出现404错误
- 检查URL是否拼写正确
- 确认Servlet类上有@WebServlet注解或web.xml配置正确
- 查看Tomcat启动日志是否有部署错误
问题2:响应乱码
- 在获取Writer前设置编码:
resp.setCharacterEncoding("UTF-8") - 同时设置ContentType:
resp.setContentType("text/html;charset=UTF-8")
4. Servlet核心功能深入
4.1 请求参数处理
获取GET/POST参数的几种方式:
java复制// 单个参数
String name = req.getParameter("name");
// 多值参数(如复选框)
String[] hobbies = req.getParameterValues("hobby");
// 获取所有参数名
Enumeration<String> paramNames = req.getParameterNames();
经验之谈:永远不要假设参数存在,要先做null检查。我曾在生产环境因为一个缺失的参数导致整个服务崩溃。
4.2 会话管理
HttpSession的使用示例:
java复制// 获取session,不存在则创建
HttpSession session = req.getSession(true);
// 设置会话属性
session.setAttribute("user", currentUser);
// 获取属性
User user = (User) session.getAttribute("user");
// 使会话失效
session.invalidate();
4.3 过滤器(Filter)应用
创建登录验证过滤器:
java复制@WebFilter("/*")
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession(false);
if (session == null || session.getAttribute("user") == null) {
// 未登录,重定向到登录页
((HttpServletResponse)response).sendRedirect("/login");
} else {
// 已登录,继续执行
chain.doFilter(request, response);
}
}
}
5. 高级配置与优化技巧
5.1 默认页面配置
在web.xml中设置欢迎文件列表:
xml复制<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
5.2 自定义错误页面
配置特定状态码的错误页:
xml复制<error-page>
<error-code>404</error-code>
<location>/error/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.html</location>
</error-page>
5.3 性能优化建议
- 启用异步处理(Servlet 3.0+):
java复制@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
AsyncContext context = req.startAsync();
// 执行异步操作
}
}
- 合理使用ServletContext:
- 适合存储应用级别的共享数据
- 避免存储大量数据导致内存泄漏
- 线程安全注意事项:
- Servlet实例通常是单例的
- 避免使用实例变量存储请求相关状态
- 必要时使用同步块或线程安全集合
6. Servlet与JSP的协作模式
6.1 模型1架构
传统JSP开发模式,业务逻辑直接写在JSP中:
jsp复制<%@ page import="com.example.UserService" %>
<%
UserService service = new UserService();
List<User> users = service.getAllUsers();
%>
<html>
<!-- 显示用户列表 -->
</html>
这种模式虽然简单,但维护困难,不推荐用于复杂项目。
6.2 模型2(MVC)架构
Servlet作为控制器,JSP负责视图展示:
java复制// Servlet中处理请求
req.setAttribute("users", userService.getAllUsers());
req.getRequestDispatcher("/userList.jsp").forward(req, resp);
jsp复制<!-- userList.jsp -->
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
</tr>
</c:forEach>
7. 常见问题深度解析
7.1 中文乱码终极解决方案
GET请求乱码:
Tomcat 8+默认使用UTF-8解码URL,旧版本需要修改server.xml:
xml复制<Connector URIEncoding="UTF-8" ... />
POST请求乱码:
在第一次调用getParameter前设置:
java复制req.setCharacterEncoding("UTF-8");
响应乱码:
java复制resp.setContentType("text/html;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
7.2 文件上传实现
使用Apache Commons FileUpload:
java复制// 检查是否是多媒体表单
if (ServletFileUpload.isMultipartContent(req)) {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
if (!item.isFormField()) {
// 处理上传文件
item.write(new File("/uploads/" + item.getName()));
}
}
}
7.3 跨域问题处理
在Servlet中设置CORS头:
java复制resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "GET, POST");
resp.setHeader("Access-Control-Allow-Headers", "Content-Type");
8. 现代Web开发中的Servlet角色
虽然现在流行Spring Boot等框架,但理解Servlet至关重要:
- 框架底层原理:Spring MVC的DispatcherServlet就是标准Servlet实现
- 遗留系统维护:许多老系统仍基于纯Servlet/JSP
- 性能敏感场景:直接使用Servlet可以避免框架开销
- 自定义组件开发:过滤器、监听器等扩展点都是Servlet规范的一部分
我在实际项目中遇到过一个性能瓶颈,最终通过绕过框架直接使用Servlet API处理特定请求,性能提升了40%。这证明了基础知识的重要性。
