简介:这是一份基于Java Web的网上书店系统完整项目,适用于高校Java课程设计和期末大作业,也适合正在学习Java Web开发的读者参考。项目覆盖图书浏览、检索、分类展示、用户注册登录、购物车与订单管理等典型电商功能,源码、页面与数据库一体,导入开发环境并配置数据库后即可运行。压缩包共108个文件,包含31个Java源文件、29个JSP动态页面、13个XML配置文件、6个CSS样式文件、若干界面图片和1个SQL数据库脚本,整体大小仅6.75MB,目录按功能模块划分,便于学习时快速定位。该项目已获导师指导并通过,被评为97分的高分设计,作为期末大作业的完整方案,省去了重新设计实现的时间和精力。目前已有1709人下载学习,下载后无需修改,可直接用于提交或在此基础上扩展二次开发。
1. 基于JavaWeb的网上书店系统:期末大作业的经典选择与真实价值
"基于javaweb的网上书店系统源码+数据库"这套包,是 JavaWeb 课程期末大作业里出现频率最高的题目之一。它不是单页 Demo,而是完整可运行的项目:前台有注册、登录、图书浏览、购物车、下单,后台有图书与分类管理、订单处理,通常还带 MySQL 建表脚本和测试数据。对要交"能跑的项目+数据库设计说明"的同学,它的价值在于把 Servlet、JSP、JDBC、Session 串成完整链路;对想入门三层架构的人,它是一份能直接照着改的骨架。真正跑起来却有不少坑——版本不匹配、中文乱码、路径错误,任何一个都能让演示当天翻车。下面按"架构拆解→部署运行→排查避坑→答辩加分"的顺序,把这套系统的关键点一次讲透。
2. 网上书店系统架构拆解:JSP+Servlet+MySQL 三层结构与数据库设计
拿到源码先别急着点运行,花十五分钟把架构看懂,后面不管是跑通还是改功能都顺手得多。这一章把这类网上书店最通用的 JSP+Servlet+MySQL 组合拆开讲清楚,顺便告诉你源码包里每个目录到底在管什么。
2.1 为什么期末大作业首选 JSP+Servlet+MySQL 这个组合
多数高校 JavaWeb 课程的教学范围就到 JSP、Servlet、JavaBean、JDBC 为止,Spring Boot 并不在期末考核清单里。Servlet 当控制器接收请求,JSP 当视图展示页面,DAO 类做数据访问,刚好对应经典的 MVC 分层。答辩时老师问"三层架构怎么体现",你可以按包名一个个指出来:com.bookstore.servlet 是控制层,com.bookstore.dao 是数据访问层,JSP 是视图层,实体类在 com.bookstore.entity。这是最稳妥的答法。
选 JSP+Servlet 而不是 Spring Boot,还有一个很现实的原因:期末答辩机器往往没有外网,Spring Boot 项目在第一次构建时要拉大量依赖,一旦 maven 仓库连不上,项目直接起不来,连演示的机会都没有。JSP+Servlet 项目只需要一个 Tomcat 和一个 mysql 驱动 jar,离线也能跑。另外它的排查路径短,出错基本就是 SQL 写错、参数没取到、跳转路径不对三类,断点一打就能定位。我见过不少同学拿 Spring Boot 去交 JSP 大作业,反而被"为什么没有 web.xml""JSP 和模板引擎的区别"这类问题问住。用 JSP+Servlet 交期末大作业,是可控性最高的选择。
一个请求从点击到页面渲染,完整链路是这样的:浏览器提交表单到 /login,web.xml 或注解把地址映射到 LoginServlet 的 doPost;Servlet 里 request.getParameter 取出用户名密码,交给 UserDao 的查询方法;UserDao 通过 DBUtil 拿到 Connection,执行 SQL 返回结果;Servlet 把结果放进 request 或 session,最后 forward 到 JSP 页面或者重定向到首页。理解这条链路之后,再看任何网上书店源码都不觉得黑匣子了——页面、地址、Servlet、DAO 一一对应,层层往下找即可。
对照这条链路去读源码,有一个很实用的看代码顺序:先打开 web.xml 看欢迎页和 Servlet 映射,如果项目用的是注解方式,就在类上找 @WebServlet;再顺着表单的 action 找到对应 Servlet,接着看 Servlet 里调了哪个 DAO 方法,最后回到页面看数据是怎么输出的。整个网上书店系统无非是这条链路的多次复用——图书列表、详情、购物车、订单,全是同一套路,看懂一个等于看懂全部。这也是为什么这类源码特别适合期末学习:不是它功能多,而是它把一条链路讲完整了。
2.2 功能模块与数据库设计:从需求到建表 SQL
典型的网上书店系统分前台和后台。前台功能:用户注册、登录、退出;图书按分类浏览、按关键词搜索;图书详情页;加入购物车、修改数量、删除条目;提交订单;查看个人订单。后台功能:管理员登录;图书新增、修改、删除;分类管理;订单状态修改。这些功能落到数据库,核心是 5 张表:用户表 user、分类表 category、图书表 book、订单表 orders、订单明细表 order_item。源码包的 sql 脚本里一般会带测试数据,演示前先确认里面有图书记录,别上台才发现首页空荡荡。
CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4; USE bookstore; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, email VARCHAR(100), phone VARCHAR(20), address VARCHAR(200), reg_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, description VARCHAR(200) ); CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, author VARCHAR(50), price DECIMAL(10,2), stock INT DEFAULT 0, category_id INT, image VARCHAR(200), description TEXT, FOREIGN KEY (category_id) REFERENCES category(id) ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, total_price DECIMAL(10,2), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, status VARCHAR(20) DEFAULT '未支付', FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT, book_id INT, quantity INT, price DECIMAL(10,2), FOREIGN KEY (order_id) REFERENCES orders(id), FOREIGN KEY (book_id) REFERENCES book(id) );这段 SQL 里有几个点,是期末答辩的高频提问。价格用 DECIMAL(10,2) 而不用 FLOAT,因为浮点算钱会有精度误差,总价对不上账是电商系统的硬伤;订单和订单明细拆成两张表,是为了一个订单装多本不同的书,明细表里单独存一份下单时的价格快照,这样以后图书改价,历史订单金额不会被带偏;user 表 username 加了 UNIQUE 约束,从数据库层面挡住重复注册。建库时显式指定 utf8mb4 而不是默认字符集,能避免一多半的中文乱码问题。
再说测试数据。源码包的 sql 脚本里一般会带几本图书的 INSERT 语句和几个测试账号,这是演示的生命线。导入完成后务必查一下 book 表有没有数据,没有就自己补几条,封面图片路径要写成相对 images 目录的真实文件名,不然列表页图片全挂。管理员账号一般也写在脚本里,搜索 INSERT INTO user 语句就能看到密码,记得在演示前改成一个自己记得住的值。
2.3 源码目录结构:每层代码该看哪里、改哪里
拿到包先解压,把目录结构过一遍再动手。典型结构长这样:
bookstore/ ├── src/ │ ├── com.bookstore.util/ 工具类:DBUtil 数据库连接 │ ├── com.bookstore.entity/ 实体类:User、Book、CartItem、Order │ ├── com.bookstore.dao/ DAO:UserDao、BookDao、OrderDao │ └── com.bookstore.servlet/ 控制层:LoginServlet、CartServlet、OrderServlet ├── web/ │ ├── WEB-INF/ │ │ ├── lib/ mysql 驱动 jar 放这里 │ │ └── web.xml 部署描述符 │ ├── index.jsp 首页图书列表 │ ├── login.jsp / register.jsp │ ├── cart.jsp / order.jsp │ └── admin/ 后台管理页面 └── sql/ └── bookstore.sql 建库脚本与测试数据按这个分层,改功能的思路很清晰:页面在 web 目录下,业务判断在 servlet 包里,查数据的 SQL 在 dao 包里,数据库连接统一在 util 包的 DBUtil。经常要动的就两处:一是数据库账号密码,二是驱动 jar 是否存在。web.xml 里的 welcome-file 决定访问根路径时先打开哪个页面,一般配置成 index.jsp;WEB-INF/lib 是放 mysql 驱动 jar 的地方,源码包里如果没有这个 jar,从网上下一个对应版本的放进去,否则启动后所有数据库操作都会报驱动找不到。这两个位置是部署报错的高发区,提前确认能省不少事。
数据库连接代码集中在 DBUtil 里,最常改的就是账号密码,只需要动一处常量:
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/bookstore" + "?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "root"; 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); } }DBUtil 里有两个参数决定你能不能连上库。URL 中的 characterEncoding=utf8 和建库时的 utf8mb4 配套,保证中文写入读出不错乱;serverTimezone=Asia/Shanghai 是连 MySQL 8 的强制要求,不加会报时区错误。驱动类名要注意对应关系:MySQL 5.x 用 com.mysql.jdbc.Driver,MySQL 8.x 必须写成 com.mysql.cj.jdbc.Driver,写错了就 ClassNotFoundException。USER 和 PASSWORD 改成你自己的 MySQL 账号,源码包里默认写的 root,实际密码不同就连不上。
DAO 层代码建议重点看登录查询这个函数,因为它最能体现代码质量。好的写法用 PreparedStatement 占位符,而不是拼字符串:
public User findByUsernameAndPassword(String username, String password) { String sql = "SELECT * FROM user WHERE username=? AND password=?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; }为什么不能用 Statement 直接拼 SQL?因为用户名里如果输入 ' OR '1'='1 这类内容,拼接出的 SQL 会绕过密码判断,这就是经典的 SQL 注入。PreparedStatement 用 ? 占位并预编译,传入值只会被当作字符串处理,注入语句不再生效。答辩时如果老师问"你考虑过安全问题吗",把这个点答出来,比背十页 PPT 都管用。源码包里如果发现用的是 Statement 拼接,建议改成上面这种写法,改动量很小但对评分影响很大。
3. 把网上书店源码跑起来:版本搭配、数据库导入与 IDEA 部署
这一章的目标只有一个:让浏览器里出现书店首页。先后顺序很重要——先定版本,再导数据,最后部署,顺序反了容易连不上库还以为是项目坏了。
3.1 版本搭配:JDK 1.8 + Tomcat 9 + MySQL 8.0 最稳
拿到期末源码,第一件事不是装环境,而是确认目标版本。我长期用的组合是 JDK 1.8 + Tomcat 9.0.x + MySQL 5.7 或 8.0。为什么不用更高的版本?Tomcat 10 之后把 Servlet 规范的 javax 命名空间整体改成了 jakarta,绝大多数期末源码的 import 还停留在 javax.servlet,在 Tomcat 10 下直接编译失败;JDK 用 1.8 而不是 11/17,是因为老项目在更高 JDK 上虽然多半能跑,但配套的编译选项和 IDE 支持容易出幺蛾子,没必要在期末阶段给自己加风险。
| 组件 | 建议版本 | 说明 |
|---|---|---|
| JDK | 1.8 | 与老项目兼容性最好 |
| Tomcat | 9.0.x | 使用 javax 命名空间,匹配源码 |
| MySQL | 5.7 或 8.0 | 5.7 省配置,8.0 需加时区参数 |
| mysql-connector | 与 MySQL 同代 | 5.1.49 配 5.7,8.0.x 配 8.0 |
mysql 驱动 jar 的版本要跟着数据库走:连 MySQL 5.7 用 5.1.49,连 MySQL 8.0 用 8.0.x。版本混搭不是不能跑,但经常出现握手协议、认证插件相关的报错,排查起来很费时间。驱动 jar 放在 web/WEB-INF/lib 目录下,Tomcat 启动时会自动加载。版本配好之后还有一个容易被忽略的步骤:确认源码包里这个 jar 确实存在。很多人以为 IDEA 会自动带依赖,实际上这种传统 Web 项目不会,缺 jar 的表现很统一:页面静态能开,一点登录或图书列表就报 ClassNotFoundException com.mysql.jdbc.Driver。
3.2 导入数据库:命令行与图形工具两种方式
先把 MySQL 服务启动,然后导入脚本。Windows 下如果 mysql 命令提示找不到,先进 MySQL 安装目录的 bin 目录再执行。导入分两步,先建库后导入:
mysql -uroot -p CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4; EXIT;mysql -uroot -p bookstore < bookstore.sql第一条命令先创建一个 utf8mb4 编码的库,第二条把源码包里的 bookstore.sql 导入到 bookstore 库。注意 < 后面要写成脚本的实际路径,路径里有空格时最好先把脚本复制到 D 盘根目录再执行,避免控制台解析路径出错。导入完成后进库确认一下:
mysql -uroot -p USE bookstore; SHOW TABLES; SELECT COUNT(*) FROM book;提示:如果 SHOW TABLES 显示的表名和源码 DAO 里查询的表名不一致,所有页面都会报"表不存在"。遇到这种情况以源码为准,改脚本或改代码,选改动量小的一边。
用 Navicat 导入更直观:连接 MySQL 后打开 bookstore 库,右键"运行 SQL 文件",选中 bookstore.sql 执行。注意如果脚本开头没有 USE bookstore 语句,一定要先选中 bookstore 库再运行,否则表会建到默认库里,之后代码怎么都查不到数据。如果连库时报 Public Key Retrieval is not allowed,这是 MySQL 8 的认证插件导致的,在 DBUtil 的 URL 末尾追加 allowPublicKeyRetrieval=true 即可解决。root 密码连不上的情况,多半是 URL 里密码写错,仔细核对 USER 和 PASSWORD 两个常量,不要急着怀疑代码。
3.3 IDEA 部署:Artifacts、Tomcat 与访问路径
IDEA 里跑 Tomcat 项目需要两步配置,缺一步都会报"没有部署工件"或 404。先配 Artifacts:File → Project Structure → Artifacts → 点 + 号 → Web Application: Exploded,把项目根目录选进去。再配运行环境:Run → Edit Configurations → 点 + 号 → Tomcat Server → Local,选中本地 Tomcat 9 的安装目录,在 Deployment 选项卡里点 + 把刚才的 Artifact 加进去。重点看 Application context 这一栏,它决定访问路径:填 /bookstore,启动后访问 http://localhost:8080/bookstore/;填 / 则通过根路径访问,但容易和本机其他项目冲突,建议保留上下文路径。
配置完成后,确认 Server 选项卡里 JRE 指向 JDK 1.8,HTTP port 保持 8080。点击启动按钮,IDEA 控制台出现 "Server startup in xxx ms" 就算启动成功。另一个高频问题是启动时报 "No artifacts marked for deployment",根源都是 Artifacts 没建或者 Deployment 列表为空,把两个配置点重新检查一遍即可。如果你习惯 Eclipse 或外置 Tomcat,把项目打成 war 包放进 Tomcat 的 webapps 目录也能跑,但改代码得重新打包,调试效率低。IDE 内置 Tomcat 的好处是 JSP 修改后刷新页面即可看到变化,不用重启服务,演示前调样式和文案会很省时间。
启动后先在浏览器输入 http://localhost:8080/bookstore/ 看首页。如果看到 Tomcat 默认欢迎页而不是书店首页,说明访问路径不对或项目没部署成功,回头检查 Application context 是否填正确。如果出现 404 而无任何报错,多半是部署列表里没加 Artifact,或选的模块不是当前项目。
3.4 功能链路验证:从注册到下单怎么证明跑通
环境起来只是开始,演示前一定要亲自走完整条业务链路。按下面顺序验证:注册一个测试账号,用同一账号登录,看首页图书列表是否带出数据库数据,点进图书详情,加入购物车,修改数量再删除一条,提交订单,最后在"我的订单"页面看到这条记录。每一步都对应一次真实数据库操作,任何一步报错都说明某个环节的参数、路径或表结构有问题。
验证时盯住两个信息源:浏览器 F12 的 Network 面板和 IDEA 控制台。点击加购后 Network 里出现 500,切到控制台看堆栈,最常见的两类是 SQLException,表示表名或字段对应不上;ClassNotFoundException,表示驱动 jar 没放对位置。页面跳转出现 404,大概率是表单 action 和 Servlet 的 @WebServlet 注解地址不一致,用全局搜索把两边对照一遍。注册完成后去数据库执行 SELECT * FROM user,能看到新记录就说明 DAO 写入链路是通的。后台管理如果有默认账号,通常会写在源码包的说明文件里,常见的默认是 admin/admin,登录后台把一本书的库存改掉,再去前台详情页确认数字变了,这一步能验证后台到前台的联动。
还有一条建议单独说:演示时不要现场注册账号。提前把测试账号准备好,购物车和订单数据也预置好,演示时走"登录→加购→下单"最短链路,时间控制在三分钟内。现场注册的最大风险是表单校验、验证码这类逻辑在演示机上行为不一致,卡住就冷了场。这是期末演示的血泪经验,宁可演示内容少而精,不要贪多翻车。
4. 网上书店运行常见问题排查:乱码、404 与启动失败的避坑记录
这类期末项目翻车点高度集中,我把实际调试中出现频率最高的五个问题整理成清单,每条按"现象→原因→解决"展开,遇到问题直接对号入座,能省下大量试错时间。
4.1 Tomcat 启动后 JSP 全部编译报错:javax 与 jakarta 的版本陷阱
现象:Tomcat 能正常启动,首页 HTML 能打开,但一访问 login.jsp 或 index.jsp 就报 500,控制台堆栈出现 Unable to compile class for JSP,或者提示找不到 javax.servlet 相关类。
原因:Tomcat 10 起把 Servlet 规范从 javax.* 整体迁移到 jakarta.*,而这套源码里所有 Servlet 和 JSP 指令都是 javax 写法,在 Tomcat 10 下编译期过不去。这不是项目坏了,是运行时版本超前了,这也是我把 Tomcat 9 列为标准配置的根本原因。
解决:把 Tomcat 换成 9.0.x 即可,项目代码一个字都不用改。在 IDEA 的 Run Configuration 里把 Tomcat Server 指向本地的 Tomcat 9 安装目录,重启即可。判断是不是这个问题的捷径:换版本之前先看报错堆栈第一行,只要出现 jakarta 或 javax.servlet 字样,九成是这个版本陷阱。如果因为某些原因必须用 Tomcat 10,就得全局替换源码和 JSP 里的 javax.servlet 为 jakarta.servlet,同时更新 web.xml 的命名空间声明,改动面很大,期末阶段不建议走这条路。
4.2 页面中文全是问号:编码问题的三个真正来源
现象:图书名、用户名等中文内容显示成问号或乱码,英文数字正常;或者数据库里明明是正常中文,页面上读出来却是乱码;再或者写入数据库时就变成了问号。
原因:这类项目的乱码有三个固定来源,分别是数据库表字符集不是 utf8、JDBC 连接 URL 没指定编码、JSP 页面没声明 UTF-8。任何一个环节没对齐,中文链路就断在那一环。最常见的场景是建库时没指定字符集,MySQL 默认 latin1 根本不支持中文;其次是 DBUtil 的 URL 少了 characterEncoding=utf8 参数。
解决:三层全部对齐,只改一层不叫根治。数据库层,建库脚本里指定 DEFAULT CHARACTER SET utf8mb4,已有库执行 ALTER DATABASE bookstore DEFAULT CHARACTER SET utf8mb4 修正;连接层,URL 加上 useUnicode=true&characterEncoding=utf8,参考前面 DBUtil 的写法;页面层,每个 JSP 顶部确认有 <%@ page contentType="text/html;charset=UTF-8" %> 声明。都改完以后重启 Tomcat,并重新导入一次数据,再刷新页面查看效果。这里有个容易迷惑的现象:有时候页面正常但数据库里是问号,看起来像数据库坏了。其实只要数据从来没以正确编码写入过,是查不出真实中文的,排查顺序应该是先清掉乱码数据,把三层编码对齐后重新插入,再看结果,而不是在乱码数据上反复刷新。如果还有表单提交后乱码的情况,在 Servlet 的 doPost 最前面加 request.setCharacterEncoding("UTF-8"),或者在项目里加一个编码过滤器统一处理。
4.3 登录成功却被弹回登录页:Session key 与重定向路径
现象:注册和登录都提示成功,但登录后点击"购物车"或"提交订单",页面又跳回 login.jsp;有些情况是反复重定向,浏览器提示"页面跳转次数过多"。
原因:两个典型原因。第一,Servlet 判断登录状态用的是 session.getAttribute("user"),但登录成功时往 session 里放的是别的 key,比如 userInfo、loginUser,存值和取值不一致,等于永远取不到用户信息,于是被当作未登录处理。第二,未登录跳转代码用了相对路径 sendRedirect("login.jsp"),在带 /bookstore 上下文的项目里,跳转地址被拼错,形成循环。
解决:全局搜索 setAttribute 和 getAttribute,把所有 session 的 key 统一成同一个字符串。这是一个很隐蔽的踩坑点,定位可能要一小时,改起来十分钟。再把所有 sendRedirect 和 forward 的路径检查一遍,统一加 request.getContextPath() 前缀,例如 response.sendRedirect(request.getContextPath() + "/login.jsp")。改完重新走一遍登录、加购、下单链路验证。还有一个 session 层面的坑容易被忽略:IDEA 里配置了热部署的话,改 Servlet 代码触发自动重启时 session 会被清空,浏览器里保留的登录态会突然失效。演示时如果刚改完代码就去点购物车被弹回登录页,不要慌,重新登录一次即可,这不属于代码缺陷,但首次遇到的人往往会误判成登录逻辑写错了。
4.4 图书图片不显示:静态资源的三种处理方式
现象:列表页能显示书名和价格,图片位置是破图或空白,F12 里图片请求返回 404;后台新上传的图片刷新后依然看不到。
原因:本质是图片资源的访问路径不对。很多源码把图片路径存成相对路径 images/xx.jpg,而页面当前 URL 是 /bookstore/bookDetail.jsp,浏览器解析相对路径时拼的是 /bookstore/images/xx.jpg,如果图片实际在 web/images 下其实能访问,问题多半出在数据库里存的路径带上了多余层级。另一种常见情况是后台通过表单上传,把图片保存在本地磁盘的某个目录(比如 D:/upload),而 Tomcat 默认不暴露磁盘路径,外网访问必然 404。
解决:前端 JSP 统一用绝对路径输出图片:,这样无论页面在哪个层级都能拼出正确地址。磁盘上传目录的场景,需要在 Tomcat 的 server.xml 的 Host 节点下加一个虚拟路径映射:
<Host name="localhost" appBase="webapps"> <Context docBase="D:/upload" path="/upload" /> </Host>配置里 docBase 是图片实际磁盘目录,path 是访问时的虚拟路径,加完重启 Tomcat,图片访问地址变成 http://localhost:8080/upload/xx.jpg。这个配置在答辩演示机上也适用,提前把图片复制到目标机器的对应目录即可。注意修改 server.xml 前先备份,改错结构会导致 Tomcat 起不来。资源路径问题还有一个变种:项目部署后 CSS 和 JS 也加载不出来,页面完全裸奔,原因和图片一样,是 JSP 里引用了相对路径,修复方式一致,统一加 ${pageContext.request.contextPath} 前缀。检查时可以直接看 F12 Console 里的所有 404 资源请求,一次全改掉。
4.5 端口 8080 被占用导致启动失败
现象:IDEA 启动 Tomcat 时,控制台直接报 Port 8080 required by Tomcat ... is already in use,或者 Tomcat 启动后很快自动停止,浏览器访问 8080 打开的是别的页面。
原因:本机有别的进程占用了 8080 端口,常见的是另一个 Tomcat 实例、某个调试端口,或某个后台服务。这不算代码问题,但最容易让人误以为项目部署错了,白折腾半天。
解决:先用命令确认占用者,Windows 下执行 netstat -ano | findstr 8080,最后一列是占用进程的 PID,到任务管理器里结束对应进程后重启 Tomcat。如果那个进程不能动,直接把 Tomcat 端口改掉:IDEA 的 Run Configuration 里 Server 选项卡把 HTTP port 改成 8081,访问地址同步改成 http://localhost:8081/bookstore/。改端口时要注意,项目里如果存在硬编码 localhost:8080 的地址,比如某些 JS 里的 ajax 请求路径,要一并替换,否则页面局部功能会调不通。如果演示机是机房公共电脑,8080 被占用的概率很高,而且你往往没有权限结束别人的进程,最稳妥的做法是在演示前就把端口改成 8081 或 9090 并跑通一次,不要等到现场再改。改完把访问地址存成桌面快捷方式,演示时直接打开,少一步输入地址的动作,就少一次出错机会。
5. 交付前值得做的三件事:自测清单、分页与登录拦截
5.1 演示前一天过一遍自测清单
把核心链路完整走一遍:注册、登录、退出各一次;图书列表和详情正常打开;加购、改数量、删除购物车条目;提交订单后个人订单列表出现记录;后台新增一本书,前台立刻能看到;后台修改库存,前台详情页数字同步变化。全部通过,项目基本就稳了。数据库里提前放好 3-5 本图书测试数据,并保证每张表都有记录,别让演示时首页空荡荡。
5.2 低成本加分项:给图书列表加分页
期末项目大多没做分页,加上分页是性价比最高的改造。改三个点:DAO 查询方法加 LIMIT 参数,Servlet 接收页码并计算偏移量,JSP 底部生成页码链接。核心 SQL 一句就能说清:
SELECT * FROM book ORDER BY id LIMIT ?, ?;第一个 ? 是偏移量 (page-1)*pageSize,第二个 ? 是每页条数。答辩时可以从"大数据量下全表查询的性能问题"切入,说明分页如何减少单次查询的数据量。代码量不大,但它是老师最容易一眼看出的"主动设计"痕迹。
5.3 必做改造:用过滤器统一登录校验
很多源码只在购物车、订单页面里分别判断登录,漏掉一个页面就能被直接绕过。更规范的方案是加一个 Filter:拦截需要登录的 servlet,未登录一律重定向到 login.jsp,同时放行登录、注册、静态资源三类路径,避免拦截器把自己拦住。Filter 的 doFilter 里判断 session 是否包含 user,有则放行,没有就跳登录页。改完后把需要保护的 URL 写进 web.xml 的 filter-mapping。
我养成的习惯是:拿到任何期末源码包,先跑通再改代码,跑通之前不动任何业务逻辑;每次只改一处验证一处,避免一次改十处出了问题不知道怪谁。期末阶段最怕的就是进入"不知道哪里坏了"的黑匣子状态,把版本锁死、把编码对齐、把路径统一,这三点做到位,这套网上书店系统基本不会在演示当天给你意外。希望帮到你。
本文还有配套的精品资源,点击获取