☰
JavaWeb核心Servlet:生命周期与MySQL项目实战
2026/9/30 3:09:04 网站建设 项目流程

1. 从 JavaWeb 到 Servlet:先搞懂它到底在解决什么问题

做了这么多年 Java 后端,我见过太多初学者一上来就抱着 Spring Boot 啃,结果连一个最基础的请求是怎么被处理、响应是怎么返回的都没搞明白。等到面试被问到 Servlet 生命周期,或者项目里遇到一个奇怪的 404,整个人就懵了。所以这篇我不打算讲什么高大上的架构演进,就老老实实把 JavaWeb 最底层的 Servlet 讲透,顺带带你手写一个完整的项目案例,配 MySQL 那种。

先回答一个很多人都问过我的问题:JavaWeb 到底是什么?说的直白一点,就是“用 Java 技术开发的 Web 应用”,也就是浏览器访问后台服务器那套东西。而 Servlet 就是这套东西里最核心的“请求处理器”。你可以把 Servlet 理解为饭店里的服务员——客人(浏览器)点菜(发请求),服务员(Servlet)把菜单传给后厨(业务逻辑代码),再把做好的菜(响应数据)端回给客人。没有服务员,客人就只能干瞪眼。

那为什么现在大家天天说 Spring Boot,还要学 Servlet?因为 Spring Boot 的底层就是 Servlet。Spring MVC 的核心 DispatcherServlet 本身就是一个 Servlet,只不过它在 Servlet 的基础上包了一层又一层,让你不用直接面对那些繁复的 API。你把 Servlet 学会了,后面看 Spring MVC 的源码就跟看自家后院一样,特别通透。

这篇文章适合谁看?不管你是在校学生准备 Java 方向的实习面试,还是自学 JavaWeb 想做一个完整的课设项目,亦或是已经写了几年 CRUD 但从来没认真捋过 Servlet 生命周期的老开发,我建议你都花十分钟读一遍。我会把 Servlet 生命周期、IDEA 里运行 JavaWeb 项目的配置、以及一个带 MySQL 的完整案例都串起来讲,保证你能直接上手复制。

2. Servlet 生命周期:为什么面试官总爱问这个

2.1 生命周期的三个关键节点

Servlet 生命周期是 JavaWeb 面试里出场率最高的题,也是理解整个 Servlet 运行机制的钥匙。它的生命周期非常清晰,就三个阶段:init、service、destroy,外加一个实例化过程。很多背八股文的人能一字不差地背出来,但你要问他“为什么 Tomcat 默认只创建一次 Servlet 实例”,他就答不上来了。

先看一个最简单的 Servlet 代码,我在项目里最常用的写法:

package com.example.demo.servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class HelloServlet extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { super.init(config); System.out.println("1. Servlet 实例创建,执行 init()"); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("3. 处理请求,执行 service()"); resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("<h1>Hello JavaWeb</h1>"); } @Override public void destroy() { System.out.println("4. 容器关闭,执行 destroy()"); super.destroy(); } }

这段代码演示的就是完整生命周期。那整个流程到底是怎么走的?我用一个实际请求拆给你看:

第一步,加载和实例化。Tomcat 容器启动的时候,会根据配置文件(web.xml 或注解)找到这个 Servlet 类,通过反射调用newInstance()创建对象。这里有一个很多人忽略的细节:默认情况下,Tomcat 是在第一次收到请求时才创建实例,而不是服务器启动时就创建。什么叫“默认情况下”?就是 web.xml 里没有配置<load-on-startup>标签时。如果你希望服务器一启动就创建,可以在 web.xml 里加上<load-on-startup>1</load-on-startup>,那个数字越小,启动优先级越高。

第二步,初始化。实例创建完后,容器立刻调用init()方法,并传入一个ServletConfig对象。init()在整个生命周期里只执行一次,所以适合做数据库连接池的初始化、读取配置文件参数这类“一次性准备”的工作。注意,ServletConfig里能拿到初始化参数,比如你可以在 web.xml 里配置:

<servlet> <servlet-name>helloServlet</servlet-name> <servlet-class>com.example.demo.servlet.HelloServlet</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </servlet>

然后在init()里用config.getInitParameter("encoding")取出来。

第三步,处理请求。每来一个请求,容器就会调用service()方法。HttpServlet 的service()方法内部会根据请求方式(GET、POST、PUT、DELETE 等)自动分发到doGet()或doPost()等方法。这一步是整个生命周期里执行频率最高的,也是并发环境下你需要重点关注的。

第四步,销毁。当 Tomcat 容器关闭,或者检测到该 Servlet 需要卸载时,容器会调用destroy()方法释放资源。同样只执行一次,适合关闭数据库连接池、清理线程池等收尾工作。

2.2 单实例多线程:Servlet 的并发模型

关于生命周期,有一个更深层的机制你必须理解——Servlet 是多线程的。这是面试官最爱追问的点。

Tomcat 默认采用单实例多线程模型,整个应用里一个 Servlet 就只有一个实例,但会有多个线程同时调用这个实例的service()。这意味着什么?意味着如果你在 Servlet 里定义了成员变量,比如:

public class UnsafeServlet extends HttpServlet { private int count = 0; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { count++; System.out.println("当前 count 值:" + count); } }

那么在高并发下,count++这行代码就存在线程安全问题。多个线程同时读到 count=1,同时加一,最后可能还是 2。这样的 bug 在测试环境根本复现不出来,一上线就冒烟。

我自己的习惯是:Servlet 里只定义局部变量,不定义可变的成员变量。如果实在需要共享状态,就加锁或者用原子类。这是新手最容易踩的坑,也最能体现你有没有真正理解生命周期背后的含义。

顺便说一句,service()方法里是可以拿到请求相关信息的,比如:

@Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); String uri = req.getRequestURI(); String param = req.getParameter("name"); }

HttpServletRequest封装了请求行的所有信息,HttpServletResponse则负责输出响应。这两个对象是容器每次请求时新建的,线程之间互不干扰,可以放心使用。

3. 手把手做一个 Servlet + MySQL 的完整案例

3.1 项目结构和数据库设计

光讲理论没意思,我直接带你写一个能跑起来的完整案例。功能很简单:做一个用户列表页面,数据库里存几张用户表,前端展示所有用户信息,通过 Servlet 访问数据库并返回数据。

先说技术选型:Servlet 4.0 + Tomcat 9 + MySQL 8.0 + IDEA 2023 + Maven。实际开发中我用的是这些版本,你只要保证版本兼容就不会出幺蛾子。Tomcat 9 对应 Servlet 4.0,Tomcat 10 对应 Servlet 5.0 但包名变成了jakarta.servlet,这里注意一下别混用。

数据库表结构,我偷个懒,设计一个非常简单的用户表:

CREATE DATABASE IF NOT EXISTS javaweb_demo DEFAULT CHARACTER SET utf8mb4; USE javaweb_demo; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(50) NOT NULL COMMENT '用户名', password VARCHAR(100) NOT NULL COMMENT '密码', email VARCHAR(100) DEFAULT NULL COMMENT '邮箱', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO t_user (username, password, email) VALUES ('zhangsan', '123456', 'zhangsan@test.com'), ('lisi', '123456', 'lisi@test.com'), ('wangwu', '123456', 'wangwu@test.com');

这里有个细节我要特意强调:字符集一定要选 utf8mb4,不是 utf8。utf8 在 MySQL 里最多只支持 3 个字节,遇到 emoji 表情或者某些生僻字就会报错。utf8mb4 是 utf8 的超集,兼容性更好。我在项目里踩过这个坑,数据库建好之后一堆中文乱码,排查了半天才发现是建表语句的问题。

项目和表结构理清之后,接下来搭建 IDEA 的 JavaWeb 项目配置。这一步是很多初学者的拦路虎,我单独拿出来讲。

3.2 IDEA 创建 JavaWeb 项目并配置 Tomcat

现在用 IDEA 新建一个 JavaWeb 项目。网上很多老教程还在教你怎么建普通的 Java 项目然后手动加 Web 支持,太麻烦了。我推荐直接用 Maven 骨架,步骤很简单:

  1. IDEA 选择 File -> New -> Project,左侧选择 Maven。
  2. 勾选 Create from archetype,然后选择org.apache.maven.archetypes:maven-archetype-webapp这个骨架。
  3. GroupId 填com.example,ArtifactId 填servlet-demo,Version 默认即可。
  4. 项目创建完成后,IDEA 会自动生成标准的 Maven Web 目录结构:
    • src/main/java:存放 Java 源码
    • src/main/resources:存放配置资源文件
    • src/main/webapp:存放 JSP、HTML、静态资源
    • src/main/webapp/WEB-INF/web.xml:Web 部署描述文件

注意,这里有一个 IDEA 的经典坑:部分版本的 IDEA 创建出来的 webapp 目录下没有WEB-INF/web.xml文件。没有web.xml并不会影响项目启动,因为 Servlet 3.0 之后支持注解开发,但如果你是新手,强烈建议手动补上这个文件,原因有两点:一是很多老教程和面试题还是基于 web.xml 的配置方式,二是当你要配置load-on-startup、初始化参数、欢迎页面时,web.xml 比注解要直观得多。

补上 web.xml 后,内容长这样:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <display-name>servlet-demo</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>

接着配置 Tomcat。在 IDEA 右上角点击 Add Configuration,选择 Tomcat Server -> Local。Name 随意填,然后配置 Application Server,选择你本地解压好的 Tomcat 目录。这一步只需要做一次,以后项目都能复用。

配置好之后,再设置 Deployment。点击 Deployment 页签里的加号,选择 Artifact — 选servlet-demo:war exploded。这里我强烈推荐用war exploded而不是 war 包,因为 exploded 模式是解压后的目录,IDEA 和 Tomcat 之间不需要打包解压的过程,修改代码后热部署速度快很多,开发调试效率倍增。

最后检查 Server 页签的 URL 设置,默认可能是http://localhost:8080/servlet-demo/,没问题,改不改都行。

至此环境就配好了。点击右上角的绿色小虫子图标(Debug 模式)启动 Tomcat,启动成功后浏览器访问http://localhost:8080/servlet-demo/,看到 Hello World 页面就代表环境没问题。

3.3 编写 JDBC 工具类和用户查询 Servlet

环境通了之后,我们开始写代码。第一步是加 MySQL 驱动的 Maven 依赖。在pom.xml里加入:

<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies>

注意javax.servlet-api的 scope 一定要是provided,意思是编译时需要,但打包时不需要打进 WAR 包,因为 Tomcat 容器本身就带有 Servlet API,打进去反而会冲突。我见过有人把这个依赖打成默认的 compile scope,结果部署到服务器时出现NoClassDefFoundError,排查了半天,最后发现是依赖冲突。

然后是 JDBC 工具类,专门负责获取数据库连接。我用的是最传统的 DriverManager 方式:

package com.example.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/javaweb_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false"; private static final String USER = "root"; private static final String PASSWORD = "你的数据库密码"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

这几个参数我展开解释一下。serverTimezone=Asia/Shanghai是 MySQL 8.0 的硬性要求,不写的话会直接报时区错。useSSL=false是因为本地开发根本没有配置 SSL 证书,不开反而更省事。useUnicode=true&characterEncoding=utf8是保证数据库读写中文不乱码的关键,字面意思是连接时使用 Unicode 字符集,编码格式为 UTF-8。

写完工具类,接着写一个标准的用户实体类:

package com.example.entity; import java.util.Date; public class User { private Integer id; private String username; private String password; private String email; private Date createTime; // 无参构造 + 有参构造 public User() {} public User(Integer id, String username, String password, String email, Date createTime) { this.id = id; this.username = username; this.password = password; this.email = email; this.createTime = createTime; } // getter 和 setter 方法,这里省略 // 每个字段对应一组 getXxx() / setXxx() 方法 }

实体类就是个 POJO,对应 t_user 表的一条记录。虽然代码啰嗦,但这是 JavaWeb 最标准的写法。后面你用 MyBatis、Hibernate 的时候,实体类的概念也一模一样。

然后写核心的 DAO 层,负责查询数据库:

package com.example.dao; import com.example.entity.User; import com.example.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class UserDao { public List<User> findAll() { List<User> userList = new ArrayList<>(); String sql = "SELECT id, username, password, email, create_time FROM t_user"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setEmail(rs.getString("email")); user.setCreateTime(rs.getTimestamp("create_time")); userList.add(user); } } catch (SQLException e) { e.printStackTrace(); } return userList; } }

这个findAll()方法我用了 JDK 7 的 try-with-resources 语法,Connection、PreparedStatement、ResultSet 会自动关闭,不用手动写 finally。我强烈建议你从一开始就养成这个习惯,因为数据库连接是宝贵的资源,忘记关闭在高并发下分分钟把连接池打满,整个应用直接卡死。

最后写我们的核心 Servlet——UserListServlet:

package com.example.servlet; import com.example.dao.UserDao; import com.example.entity.User; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @WebServlet("/user/list") public class UserListServlet extends HttpServlet { private UserDao userDao = new UserDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=UTF-8"); List<User> userList = userDao.findAll(); StringBuilder html = new StringBuilder(); html.append("<!DOCTYPE html>"); html.append("<html lang='zh-CN'>"); html.append("<head><meta charset='UTF-8'><title>用户列表</title></head>"); html.append("<body>"); html.append("<h1>JavaWeb 用户列表</h1>"); html.append("<table border='1' cellpadding='8' cellspacing='0'>"); html.append("<tr><th>ID</th><th>用户名</th><th>邮箱</th><th>创建时间</th></tr>"); for (User user : userList) { html.append("<tr>"); html.append("<td>").append(user.getId()).append("</td>"); html.append("<td>").append(user.getUsername()).append("</td>"); html.append("<td>").append(user.getEmail()).append("</td>"); html.append("<td>").append(user.getCreateTime()).append("</td>"); html.append("</tr>"); } html.append("</table>"); html.append("</body></html>"); resp.getWriter().write(html.toString()); } }

注意第九行的@WebServlet("/user/list")注解,这是 Servlet 3.0 之后的新写法,告诉容器这个 Servlet 的访问路径是/user/list,不用在 web.xml 里配一堆<servlet>标签了。访问路径前面那串req.setCharacterEncoding("UTF-8")是处理 POST 请求中文乱码的关键,虽然这个案例用不上,但我习惯加上,以防后面扩展。

把这几层写完,顺序是 User -> UserDao -> DBUtil -> UserListServlet,Maven 依赖和 Tomcat 环境都配好后,启动项目,浏览器输入http://localhost:8080/servlet-demo/user/list,就能看到一个表格,里面显示着数据库 t_user 表的所有用户。

3.4 从访问路径到浏览器显示:一条请求的完整航线

上面代码跑通了,我再用讲故事的方式把整条请求链路串一遍,让你脑子里有一张完整的地图。

  1. 浏览器输入http://localhost:8080/servlet-demo/user/list。
  2. 请求到达 Tomcat 后,Tomcat 的 Connector 组件把 HTTP 请求报文解析成HttpServletRequest对象。
  3. 根据应用上下文/servlet-demo,Tomcat 定位到当前部署的这个 Web 应用。
  4. 根据 URL 里的/user/list,Tomcat 的映射器在内存中查找对应的 Servlet。它知道这个路径对应UserListServlet,因为启动时容器扫描到@WebServlet注解,已经把映射关系注册好了。
  5. 判断UserListServlet实例是否存在。不存在就先走生命周期创建并初始化,存在就直接复用。
  6. 调用service()方法,因为浏览器发出的请求是 GET,service()内部会调用doGet()。
  7. doGet()里通过UserDao.findAll()查询数据库,把返回的 List 拼成 HTML 字符串,写入HttpServletResponse的 writer。
  8. Tomcat 拿这个响应对象,按照 HTTP 协议格式组装消息,通过 Socket 发回浏览器。
  9. 浏览器拿到 HTML 源码,解析渲染,显示出用户列表表格。

这一整套链路你捋顺了,JavaWeb 的骨架就长在你脑子里了。以后不管换什么框架,底层都跳不出这条航线。

4. 常见问题与排查技巧实录

这部分是压轴干货。我把自己这些年做 JavaWeb 项目踩过的坑、以及学生和同事最常遇到的一堆糟心事全整理出来。每一条都配上排查思路,你按图索骥就行。

4.1 经典报错速查表

报错信息根本原因解决方案
ClassNotFoundException: com.mysql.cj.jdbc.DriverMySQL 驱动没有引入或没有打包pom.xml 加依赖并刷新 Maven;检查依赖 scope 是否是 provided
Communications link failureMySQL 服务没有启动,或连接地址/端口错误检查 MySQL 服务;确认 URL 里的localhost:3306是否正确
The server time zone value is unrecognizedMySQL 8.0 默认时区配置不合法JDBC URL 加上serverTimezone=Asia/Shanghai
HTTP Status 404Servlet 访问路径不存在,或注解路径冲突检查@WebServlet的路径;清理 Tomcat 缓存重启;确认访问的是项目名 + 映射路径
HTTP Status 500服务器内部异常,代码抛了运行时异常看控制台或 catalina 日志的 Caused by
中文乱码请求或响应的字符集不一致请求侧req.setCharacterEncoding("UTF-8");响应侧resp.setContentType("text/html;charset=UTF-8");数据库 JDBC URL 加上 characterEncoding=utf8
端口被占用8080 被其他进程占用`netstat -ano
NoSuchMethodError: javax.servlet...项目中 Servlet API 依赖和 Tomcat 自带版本冲突确认是否已引入javax.servlet-api;scope 改为 provided

4.2 处理 404 时的排查思路

404 是 JavaWeb 初学者遇到最多的错误,八成以上不是代码问题,而是路径问题。我给你一套排查顺序:

第一步,先确认 Tomcat 是否成功启动了应用。看 IDEA 控制台有没有Deployment started或者类似的日志,以及 Tomcat 的 localhost 日志有没有报错。

第二步,确认访问地址完整。http://localhost:8080/servlet-demo/user/list,其中servlet-demo是应用上下文路径,/user/list是 Servlet 映射路径。这两段缺一不可。有人会漏掉/servlet-demo,直接访问http://localhost:8080/user/list,那当然是 404。

第三步,确认@WebServlet注解里的路径是否正确。注意这个路径必须包含英文字符串,例如@WebServlet("/user/list")就对应 URL 的/user/list部分。大小写敏感,/User/list和/user/list是两回事。

第四步,确认 Servlet 类是否被正确加载。在doGet()里加上一行System.out.println("进入 doGet()");,重新 Debug 启动,看控制台有没有输出。如果连日志都没有,说明你的 Servlet 根本没进到处理逻辑,问题在设计 URL 映射阶段。

第五步,确认编译产物里有这个类。打开 project 目录下的target/classes文件夹,看com/example/servlet/UserListServlet.class是否存在。不存在就手动 Build 一下,这能帮你快速排除“IDEA 没编译”这种弱智级错误。

4.3 中文乱码的整套解决方案

乱码问题在 JavaWeb 里属于“经典永流传”,几乎每个用中文的人都会碰到。我在实际项目里总结了一套三管齐下的方案:

第一管:请求编码。统一在 Servlet 的service()方法开头用req.setCharacterEncoding("UTF-8"),或者使用 Filter 统一设置。这个方法是针对 POST 请求的 body 部分的编码。至于 GET 请求,乱码根源在 Tomcat 8 之前的 URL 解码,Tomcat 8 之后默认 UTF-8,问题不大。

第二管:响应编码。用resp.setContentType("text/html;charset=UTF-8")告诉浏览器响应的内容类型和编码。注意这个方法要在getWriter()之前调用,否则无效。最好再配合resp.setCharacterEncoding("UTF-8")双保险。

第三管:数据库编码。建表用DEFAULT CHARSET=utf8mb4,JDBC URL 里加useUnicode=true&characterEncoding=utf8。这三个地方只要有一个不是 UTF-8,数据就会在某个环节变成乱码。

另外提醒一句,IDEA 里Settings -> Editor -> File Encodings下的 Global Encoding、Project Encoding、Default encoding for properties files 这三项,我建议全部改为 UTF-8。否则源码文件的编码不一致,代码本身写的就是乱码,聊什么都是白搭。

4.4 改代码不生效:热部署与缓存的坑

很多新手有个体验:改了代码,重新启动 Tomcat,发现访问还是旧页面,跟中了邪一样。这个原因很多时候是 IDEA 的 Tomcat 热部署没配置对。

我建议这样配置:在 Tomcat 配置的 Server 页签里,把 On Update Action 和 On Frame Deactivation 都改成Update classes and resources。这样你每次修改 Java 代码后按Ctrl + F10,IDEA 就会热更新 class 文件,不需要重启 Tomcat。但是要注意,新增方法签名、修改注解配置、修改 web.xml 这些结构性改动,热部署是不生效的,必须要重启。

另外,浏览器的缓存也会造成“代码已改但效果没变”的假象。我在开发时习惯把浏览器开发者工具打开,勾选 Network 页签里的 Disable cache。排查的时候可以直接 Ctrl+F5 强制刷新页面,或者用无痕窗口访问,排除浏览器缓存干扰。

4.5 连接数据库报错的常见坑

数据库连接报错的排查顺序,其实是有套路的。首先看 URL 里的几个关键参数,我遇到过几次排查到深夜的问题,最后发现是localhost写成了localhsot这种手滑错误。其次看密码,MySQL 密码里如果有特殊字符,比如#、&、%,在 URL 里会被特殊解析,需要做 URL 编码。最后看防火墙和远程权限,如果是连远程数据库,确保 MySQL 的 user 表里授权了对应 host 的访问权限。

再多说一句,千万别在代码里长期硬编码数据库密码。虽然我上面的示例为了简化就是这么写的,但工作中我建议至少把数据库连接信息放到jdbc.properties配置文件里,用Properties读取,这也为后续切换到连接池留好了余地。

5. 进阶与思考:Servlet 之后还能往哪走

如果你把上面的案例完整跑通了,我建议你再做两件小事,能让这段学习经历瞬间上一个台阶。

第一件:把 JDBC 工具类换成Druid 连接池。具体改动思路是,读取druid.properties配置文件,通过DruidDataSourceFactory.createDataSource(properties)获取一个数据源对象,然后从数据源里拿连接。这一步是为以后用 Spring、MyBatis 时理解“池化思想”打基础。

第二件:把页面展示改成JSP + JSTL。Servlet 负责查数据库、把数据塞到 request 里,然后req.getRequestDispatcher("/userList.jsp").forward(req, resp)转发到 JSP 页面,在 JSP 里用c:forEach循环输出表格。这虽然是我现在工作中不太会直接用的写法,但它是理解 MVC 分层最直观的例子,也是你以后看 Spring MVC 代码的重要认知基础。

前面这些都是从开发者的角度讲的,再补一个笔试面试的角度。Servlet 相关的面试题我见过的有:生命周期是什么、Servlet 和 Filter 的区别、forward 和 redirect 的区别、Cookie 和 Session 的区别、如何实现一个 Filter 做登录校验。这五道题你现在都能答吗?如果不能,那正好说明这篇文章让你发现了自己知识地图上的空白点,这就是收获。

最后再讲一个我个人的实操体会。我在工作里已经很多年没有直接写 Servlet 了,但每次排查线上问题,打开 Spring MVC 的堆栈,看到 DispatcherServlet 那几个类的时候,心里特别有底。因为我知道底层就是那套生命周期:实例化、init、service、destroy。困扰很多人的“框架魔法”,说穿了不过是容器和 Servlet 规范之上的一次又一次封装。你把这些基础真正吃透了,以后学什么框架都是降维打击。

写这些不是为了劝你回到 Servlet 时代,恰恰相反——是为了让你用更短的时间冲过 Servlet 这个关口,然后带着清晰的认知去拥抱更新的技术。漫漫长路,把地基打好,后面才能建高楼。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询