这期笔记整理的是 JavaWeb_04,准确说是我自己往“能跑通的完整案例”方向走的时候,踩完坑之后重新梳理出来的一份记录。编号 04 是我本地笔记里的索引,它不是课程编号,而是同一套 JavaWeb 实战里第四次比较大的整理。这一篇里我不打算再讲那些零散的语法,只聊三件最要命的事:IDEA 里怎么把 JavaWeb 项目配置好、MySQL 数据怎么流畅地接进完整案例、以及启动运行时哪些错误是可以提前躲开的。
从热度靠前的几个关键词也能看出大多数人的痛点:idea 运行 javaweb 项目配置、javaweb 项目完整案例 mysql、springboot javaweb、还有那种把笔记数据整理得很全的 JavaWeb 讲义。这些词指向的都是同一个阶段——理论已经看过了,但距离“完整项目跑起来”还差得非常远。如果你正在被这一步卡住,这篇就是按我的实操顺序来写的,可以当成一份直接照着搬的配置手册。
1. 先把定位搞清楚:JavaWeb_04 到底在解决什么问题
这一节不写代码,先整理思路。很多人在网上看了不少教程,笔记抄了几大本,代码却仍然跑不起来,核心原因通常是没想清楚自己当前到底缺哪一块。
1.1 前几轮实操做了什么,为什么这次单独拿出来写
实际项目里,JavaWeb 的学习路线通常是“环境 -> 静态页面 -> Servlet -> JSP -> JDBC/MyBatis -> 完整案例”。前几轮笔记里,我已经把 Servlet 和 JSP 的基本用法都敲过了一遍:能处理请求、能渲染页面,但问题是所有代码都散落在单个 demo 里,没有真正意义上被拼成一个应用。
JavaWeb_04 的核心任务,就是把这些散点收拢成一个完整的项目骨架。骨架一旦立住,后面的功能扩展才有意义。很多初学者喜欢一上来就写业务逻辑,但业务逻辑写在哪个目录、请求从哪里进、数据层怎么接,这些结构问题不解决,代码会越写越乱。
所以这篇整理的重点不只是“能运行”,而是搭建过程中的决策逻辑。比如为什么用 Maven 而不是手动导入 jar 包,为什么分 dao/service/controller 包,为什么连接池、事务要单独设计。只有理解了这些为什么,后面换框架、换数据库,你都不会慌。
1.2 笔记数据怎么变成能用的项目
我的做法是把笔记重新拆成三步。第一步,先看项目目录结构,把自己要写的包和类提前列个清单;第二步,只保留与当前案例直接相关的 SQL、实体类、DAO 方法,其他无关代码一律不看;第三步,跟着完整案例把项目本地跑起来,遇到报错再回头翻笔记,效率比从头读高得多。
笔记永远只是参考资料,真正值钱的是跑通的路径。JavaWeb_04 的内容会特别强调这个路径:从配置环境开始,到 MySQL 建表、写后端逻辑、启动服务、验证接口,最后再倒推回去看每个配置项的含义。这样过一遍之后,哪怕你换一个项目,路径也基本是通用的。
1.3 完整案例的价值:把一次数据流跑通胜过十遍语法背诵
传统学习方法是把 API 背一遍,但有一个致命缺点:API 运行时依赖的前置状态,是文档里不会写明白的。例如 JDBC 的 Connection、Statement、ResultSet,光看代码会以为只是三步走,实际运行过程中还有驱动加载、URL 参数、数据库状态、jar 包版本匹配等隐性因素。
完整案例的学习意义就在于暴露这些隐性因素。只有真正建一个库、建一张表、写一个查询、再把结果渲染到页面上,你才会遇到时区问题、端口冲突、字符集乱码、驱动类找不到这些普遍报告。这些本身就是最好的老师。后面遇到 Spring Boot 项目,你也会发现,以前经历过的这些问题换了个壳,根子上还是一样的原理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:IDEA 里跑通 JavaWeb 项目的正确配置
配置是很多人的第一道坎。代码可以慢慢学,但环境配不对,连一个 Hello World 都跑不起来,特别消磨耐心。
2.1 选型建议:JDK、Tomcat、Maven 别凭直觉乱搭
不少同学是自己研究然后配了个乱糟糟组合:JDK 8 配最新 Tomcat 10,或者 JDK 17 配一个旧版本 Tomcat 8,最后被各种兼容问题折磨。这里我先直接给一套最稳妥的组合:JDK 8 + Tomcat 9 + Maven 3.6.3。这套组合里 Servlet API 是 javax.servlet.*,主流教程笔记都能对得上,不必为兼容性额外处理。
如果你做的是传统 Servlet 阶段,JDK 8 和 Tomcat 9 是兼容性最好的搭配。强行用 JDK 17 不是不可以,但模块化带来的反射和类加载限制,会让初学者排查问题的难度直线上升,没必要。如果你未来切到 Spring Boot 2.x,官方最低要求还是 Java 8,这套环境继续适用;切到 Spring Boot 3 才需要 JDK 17,但那是后话,不必现在铺路。
项目构建工具请一律使用 Maven。手动把 jar 包复制到 WEB-INF/lib 的办法不是不能用,但依赖传递、版本管理、打包发布全都要手工维护,项目复杂度一上来必然失控。Maven 的项目模型会让依赖清晰可见,这也是 JavaWeb 项目能一步步变成工程化项目的重要基础。
2.2 IDEA 里创建 Web 工程最容易漏的三个步骤
一步到位创建完整 JavaWeb 工程,在 IDEA 里其实有个很标准的顺序。先用 Maven 创建普通 Java 工程,再手动为它添加 Web 模块,这样做比直接勾选 Maven archetype 更干净,因为新手经常遇到 archetype 加载慢甚至失败的情况。
具体步骤是:File -> New Project -> Maven,建好普通工程后,右键项目 -> Add Framework Support -> Web Application。这种方式比直接使用 maven-archetype-webapp 骨架更稳定,能避免部分网络环境下骨架模板拉取困难导致创建失败。之后 IDEA 会为项目生成 src/main/webapp 目录。
第二步检查 src/main 下面有没有出现 webapp 目录。没出现就手动新建,同时注意目录名必须是 webapp,前端资源放这里,WEB-INF 目录下必须有 web.xml 或后期换成注解形式。如果你发现建出的普通目录没被识别成 Web 资源目录,可以在 Project Structure -> Facets 中手动添加 Web 模块,把 Web Resource Directory 指向 webapp 目录。
第三步是配置 Artifact。这个步骤很多新手最容易忽略,它的本质是把编译后的项目打包成可部署结构,保证 Tomcat 启动时可以找到页面和后端类。如果 Artifact 没有正确配置,或者用了错误的打包方式,IDEA 的 Tomcat 运行配置就会直接失败,或出现 404。
我建议开发阶段直接选 war exploded 模式。这个模式在 Tomcat 启动后读取的是编译输出目录,改完代码,Rebuild 一下,再 Debug 就能生效,不用反复打包 war 文件。war 模式则是把整个应用打成一个压缩包再发布,适合最终交付,开发调试阶段没有必要用它。
2.3 Tomcat 运行配置里那些“没有报错但就是不对”的参数
配置 Tomcat 时有几个参数,表面看无所谓,实际却影响着启动结果。第一是 Application server 下拉框是否选择了正确的 Tomcat home,有些电脑上装了多个 Tomcat 版本,IDEA 只识别其中一个,如果把 Tomcat 10 配给使用 javax.* API 的项目,就会出现 NoClassDefFoundError,看起来像依赖缺失,其实是 API 包名差异。
第二是 On 'Update' action 和 On frame deactivation 这两个下拉选项。从开发效率上看,把它们分别设为 Update resources 和 Update classes and resources 是相对省事的。但需要注意热部署不是万能的,修改了 pom.xml、注解配置或者 web.xml 后,必须重新部署,不要把所有情况都指望热更新。
第三是 VM options,绝大多数项目这里不用动,只有需要设置 JVM 内存或 JMX 监控时才使用。对入门阶段,重点是在 Project Structure 里确认 Project SDK 和 Tomcat 的 JRE 版本一致。曾见过把 Tomcat 的 JRE 默认值改成跟项目 JDK 不一致,导致启动直接退出,且不打印有效日志。
这类看不见的问题,其实可以按“最后改动”原则排查:先回忆最后一次改动配置是什么,然后逐个恢复。实在不行就回到一个确认干净的配置模板上重来一遍,比反复猜测要省时间得多。
3. 数据库接入:用 MySQL 还原一个真实业务场景
传统 JavaWeb 阶段,数据层是最有含金量的部分。数据库设计得好不好,直接影响后面所有代码的复杂度。
3.1 先建表,还是先写代码?以数据流为准
真实 JavaWeb 项目里,建议先建数据库表,再写实体类、DAO。为什么?因为后端所有操作都围绕数据展开,表设计一旦改动,实体类、SQL、页面字段都要改,代价最大。数据库结构先稳定下来,后端代码会顺很多。
以最常见的“用户管理”或者“图书管理”为例子,通常建两张表:用户表 user 和借阅记录表 borrow。建表时字段类型要对上 Java 类型,Java 的 LocalDateTime 对应 MySQL 的 datetime,Java 的 BigDecimal 对应 decimal(10,2),这一点容易出错。字符集建议在建表语句里直接指定 utf8mb4,避免后面连接串写了 UTF-8,表却是 latin1,导致中文乱码找半天。
用完整案例来看,user 表字段可以设计为 id、username、password、email、create_time;borrow 表字段为 id、user_id、book_name、borrow_time、return_time。外键关系在 JavaWeb 阶段不必非要在数据库层面建,但代码里的关联关系一定要设计清楚,否则后面做关联查询时,List 和对象的嵌套关系会一团乱。
3.2 连接数据库前,把这些依赖和配置放到位
传统 JavaWeb 推荐使用 Druid 连接池或 HikariCP 连接池。连接池的意义可以打个比方:不使用连接池,每次请求都创建新的数据库连接,相当于每次出门都重新办一张身份证,性能极差。连接池就是提前准备一批连接,请求来了直接拿,用完归还,再也不用重复建连。
在 pom.xml 里需要加几个依赖:mysql-connector-j、druid 或 HikariCP、junit、servlet-api(scope 设为 provided)。驱动版本和 MySQL 版本要匹配,MySQL 5.x 推荐 5.1.49,MySQL 8.x 则需要使用 com.mysql.cj.jdbc.Driver,驱动类名写错会直接 ClassNotFound。
数据库连接串最常错的是时区参数。MySQL 8 之后的默认时区是系统时区,JDBC 驱动要求显式写明 serverTimezone,否则会报 “The server time zone value” 的错误。除了时区,连接串还建议加上 useUnicode=true&characterEncoding=utf8&useSSL=false,这些参数的意义是保证数据传输用 UTF-8、不做不必要的 SSL 握手,避免中文乱码和握手警告。
连接配置不要在每一个类里直接用 DriverManager.getConnection(),多次创建连接会浪费资源、连接数也不好控制。更好的做法是把数据源配置写在 properties 文件里,再写一个 DBUtil 工具类,静态初始化数据源。这样后面想换数据库、换连接池,只改一个文件,而不是满项目找代码。这是很多笔记里一笔带过、但项目实践中异常重要的点。
3.3 一个完整增删改查链路的细节拆解
网上很多笔记给的都是简化代码:new 一个 Connection,拼字符串 SQL,循环读取 ResultSet。但真实的工程实践里,至少要有三层防御。第一层,SQL 不要拼接用户输入,必须使用 PreparedStatement 占位符;第二层,资源要放到 try-with-resources 里,确保连接能归还连接池;第三层,返回数据不要直接输出 ResultSet 对象,应该转换成 Java 实体对象,再组成 List 返回。
这里分享一个完整的查询代码(节选 UserDao):
java复制public List<User> findAll() {
String sql = "SELECT id, username, email, create_time FROM user ORDER BY id DESC";
List<User> users = new ArrayList<>();
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
User u = new User();
u.setId(rs.getInt("id"));
u.setUsername(rs.getString("username"));
u.setEmail(rs.getString("email"));
u.setCreateTime(rs.getTimestamp("create_time").toLocalDateTime());
users.add(u);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("查询用户失败", e);
}
return users;
}
注意几个地方。第一,try-with-resources 省了一堆 finally,关闭顺序是 Connection、PreparedStatement、ResultSet 从外到内。第二,异常不要只 printStackTrace,哪怕粗糙一点也要重新包装成 RuntimeException 抛出去,这样上层才能捕捉并做统一错误提示,否则日志里只有一句话,用户页面上却是 500。第三,查询列名要和表字段严格一致,尤其是你用了 AS 别名时,ResultSet get 也要用别名,否则就会报 column not found。
Service 层再来做业务判断:比如删除用户前先检查他有没有未归还的借书记录。service 是事务边界,多个 DAO 操作放在一个事务里,要么一起成功,要么一起回滚。纯 JDBC 里可以用 conn.setAutoCommit(false); 配合 commit/rollback 实现。等你后面用 Spring 的 @Transactional,本质还是这一层逻辑,只是省了手动代码。
4. 实操记录:从新建项目到完整运行
纸上谈兵结束,现在把整个过程过一遍。这一节是按我实际操作的顺序记录的,你可以边看边在 IDEA 里同步做。
4.1 步骤一:用一个 Maven 工程把骨架立起来
我常用的目录骨架是这样:
text复制src/main/java/wang/example/
controller/UserServlet.java
service/UserService.java
service/impl/UserServiceImpl.java
dao/UserDao.java
dao/impl/UserDaoImpl.java
entity/User.java
util/DBUtil.java
src/main/resources/db.properties
src/main/webapp/
WEB-INF/web.xml
index.jsp
css/style.css
为什么要分 controller、service、dao 三层?简单说,controller 只接收请求和返回响应,service 处理业务规则和事务边界,dao 只做 SQL 数据访问。这样做以后,接口变化只需要改 controller,数据源变化只需要改 dao,不会因为一个需求把整个项目掀个底朝天。
在 IDEA 里创建完 Maven 工程后,把 pom.xml 写入基本依赖,等待 Maven 下载。首次下载依赖会慢一点,建议设置阿里云镜像加速。这一步不是可选项,如果依赖下载失败或只下载了一部分,后面编译就会各种 ClassNotFound。
4.2 步骤二:把请求链路串联成可访问的页面
一个最简单的请求链路是这样的:浏览器输入 http://localhost:8080/user/list -> web.xml 或注解映射到 UserServlet -> 调用 UserService -> 调用 UserDao 查到 List
这里有两个非常细的点。第一,在 web.xml 配置 Servlet 映射时,url-pattern 要以 / 开头,不能写成 user/list,否则 Tomcat 启动会报错或匹配失败。第二,Servlet 注解 @WebServlet("/user/list") 和 web.xml 配置不能同时对一个 URL 生效,一旦你从 web.xml 方式转用注解方式,记得把旧配置删干净,否则同一个 url-pattern 会映射到两个类,Tomcat 启动时直接报冲突。
JSP 里不要写 Java 核心逻辑,尽量用 JSTL 和 EL 渲染数据。传统写法是 JSP 顶部 <%@ page import="java.util.List" %>,然后整页写 <% for(...) %>,这种写法调试难度极高。更可控的方式是引入 JSTL 标签库,用 <c:forEach> 遍历集合,页面只负责展示。如果项目后期要接 Bootstrap、Vue 等前端框架,这种分离会让你的迁移轻松得多。
4.3 步骤三:启动、验证、跑通全流程
配置完成后的启动顺序很固定。先确认本地 MySQL 服务已启动,然后用数据库客户端执行 SQL 脚本初始化数据。再在 IDEA 里给 Tomcat 配置一个 Deployment,点击启动按钮,观察控制台日志。
关键是要习惯读 Tomcat 日志,不要只看浏览器画面。比如浏览器 404,不一定就是项目没部署,往往是 URL 写错了,或者 Artifact 部署名和端口拼接不对。要让日志准确,启动后在控制台看 “Deploying web application directory” 以及后面的 “Finished” 字样,再去切换到 Tomcat Localhost Log,那里会打印每个应用启动的异常信息,很多隐性问题都藏在这里。
一个完整流程的最终结果,是你能从页面上看到来自数据库的用户列表。如果看不到,必然是链路里某个环节有问题。不要瞎猜,用排除法:先直接查数据库是否有数据;再写一个临时的 Java main 方法测 DAO;再通过浏览器访问 Servlet 看是否返回数据;最后再看 JSP 渲染。按这个顺序排查,基本十分钟内能定位问题。
5. 常见问题与排查技巧实录
最后这部分是最值钱的。很多问题不是代码逻辑难,而是你根本不知道去哪看,知道规律以后一次就能解决。
5.1 端口占用和启动失败
Tomcat 8080 端口占用是最常见的问题之一,报错类似于 “Port 8080 was already in use”。解决办法有三种。一是换端口,在 Tomcat 配置里把 HTTP port 改成 8081,但改完要注意访问地址也要同步改。二是把占用进程找出来结束,Windows 用 netstat -ano | findstr 8080 配合任务管理器结束 PID,macOS/Linux 用 lsof -i :8080 加 kill -9。三是如果占用的是自己上一次没关干净的 Tomcat,去 IDEA 里把上一次运行的实例 Stop 掉。
还有一个启动时容易被忽略的原因:Tomcat 的 Catalina 日志里出现 “One or more listeners failed to start”。这种通常是把本来只能由应用服务器提供的 jar 包(比如 servlet-api)打进了项目依赖。解决方法是检查 pom.xml 中 servlet-api、jsp-api 的 scope 是否为 provided,这个 scope 表示编译和测试时使用、打包时不包含,避免和 Tomcat 自带的实现冲突。
5.2 数据库连不上时,先分清是驱动、URL 还是权限问题
数据库相关报错有几条经典路径。第一条 ClassNotFoundException: com.mysql.jdbc.Driver,说明依赖没拉到或驱动类名不对,检查 pom.xml 中是否引入了正确的 mysql-connector-j,版本是否匹配。第二条 Communications link failure,一般是 MySQL 服务没启动、端口不对、或 URL 里的 host/port 写错。第三条 Access denied for user,说明账号密码或 host 授权有问题,通常是 root 密码写错,或者只允许 localhost 连接。
排查数据库问题时,建议先用数据库客户端(Navicat、DBeaver 或 MySQL Workbench)在外面验证一遍连接参数。客户端能连成功,你的 Java 代码排除 90% 的问题;客户端都连不上,那就不用去看程序日志了,先把服务和账号解决。这个习惯能省很多时间。
5.3 中文乱码,四处配置必须同步
中文乱码根源通常是四个环节不一致:数据库表字符集、JDBC 连接串、Tomcat 的 URIEncoding、JSP/Servlet 的页面编码。只要有一个不是 UTF-8,就容易出现“页面看到问号”或者“数据库里变成乱码”。
具体做法:建表时统一 DEFAULT CHARSET=utf8mb4;JDBC URL 加 characterEncoding=utf8;如果使用 GET 请求传中文,Tomcat 的 server.xml 里设 URIEncoding="UTF-8";JSP 顶部写 contentType="text/html; charset=UTF-8"。这四处对齐后基本不会再有中文问题。注意,连接串不能只写 characterEncoding,还建议在 MySQL 服务端变量里设置 character_set_server,否则新建数据库可能又回到默认 latin1。
5.4 常见问题速查表
| 现象 | 最可能原因 | 快速处理 |
|---|---|---|
| 启动后访问 404 | Artifact 没配置成 war exploded,或 URL 映射不对 | 检查 Run Configuration 中 Deployment 和 web.xml 映射 |
| 日志显示连接被拒绝 | MySQL 没启动或端口不对 | 先在本机数据库客户端试连 |
| ClassNotFound 驱动 | 没引入 mysql 驱动或驱动类名写错 | 检查 pom.xml 依赖与 Driver 字符串 |
| 中文变成问号 | 表、连接串、Tomcat URI 编码不统一 | 四处统一为 UTF-8 |
| 页面显示源码而非渲染效果 | 访问路径被当作静态文件处理,Servlet 映射错误 | 看控制台和日志,确认访问路径 |
| 数据库锁死 | 连接没关闭 | 全项目统一使用 try-with-resources |
还有一个容易被忽略的习惯:在项目根目录保存一份 README,把部署步骤、数据库初始化脚本、连接账号、常用启动命令写清楚。这看似只是“写文档”,实际是最值钱的经验沉淀。一个月后重新打开这份 JavaWeb_04 笔记时,所有可复现的信息都在一个文件里,不需要从头回忆。
最后再分享几个从这套案例里沉淀出来的小习惯
可能没有太多惊人的技巧,但对我个人帮助最大的几个点是:第一,环境配置不要一次配好就再也不碰,隔几天主动清理一次 Tomcat 缓存并重新构建,能避免把缓存里的旧 class 当成自己的新代码。第二,数据库脚本最好用独立的 .sql 文件维护,千万不要只在数据库工具里执行一遍,等换电脑或团队协作时,脚本缺失是灾难级问题。第三,不要怕在理解完整案例之前反复重开项目,重开一次,你对 IDEA 配置的理解就会加深一层。过几天你会发现,配置不再是玄学,而是项目的一部分。
JavaWeb_04 到这儿就完整翻篇了。下一次如果往 Spring Boot 方向走,你会发现前面这些经验百分之八十都能平滑迁移,因为那一套自动配置的背后,仍然是 Tomcat、MySQL、Servlet 这些真正干活的东西。先把完整案例跑通,后面的路会顺很多。
