
简介一份基于JSP技术实现的水果销售管理网站毕业设计资源主要面向Java Web方向初学者、高校学生以及需要快速搭建电商类管理系统的开发者。从技术栈看它涉及JSP动态页面、MySQL数据表设计及前后台交互逻辑适合有一定Java基础、希望理解完整项目结构的人群。系统采用B/S结构与MySQL数据库按管理员和用户两类角色划分功能管理员端涵盖个人中心、用户管理、水果信息管理、水果类别管理、销售单位管理、销售分析、会员分析、系统管理和订单管理用户前端包含首页、水果信息、新闻资讯、个人中心、后台跳转、购物车等入口。从前台选购、购物车提交到后台订单处理及销售、会员分析整体流程较完整具备商业业务参考价值。压缩包大小约23.1MB包含项目源码、毕业论文lw和答辩PPT可支持从系统设计、编码实现、文档撰写到结题汇报的毕设全流程已有51人学习/浏览适合毕业设计、课程设计或Java Web项目练习中作为蓝本也可基于源码进行二次开发与功能扩展。1. 二手旧项目这套 JSP 水果销售管理网站值不值得接手如果你是计算机专业的学生这会儿多半正被课程设计或毕业设计逼到墙角。搜了一圈发现最不缺的就是这种带“源码lwppt”三件套的 JSP 老项目。这套 JSP 水果销售管理网站光看标题就知道是十年前的经典课程设计套路一个基于 JSP Servlet MySQL 的管理系统配上论文lw和答辩 PPT打包成一个压缩包。它要解决的问题非常明确——用最传统的 Java Web 技术栈把“水果商城”的购物流程做完整用户注册登录、浏览水果、加购物车、下单管理员管商品、管订单、管用户。适合谁两类人一类是只求交差、能跑能答辩的学生另一类是刚学完 JavaWeb、想找一套完整代码把 MVC 分层看明白的初学者。但说实话这种老项目的坑比你想的多JDK 版本不对跑不起来、Tomcat 版本挑人、数据库密码写死在代码里、编码乱成一团——我见过太多人解压之后卡在第一步就放弃了。这篇文章就按我实际接手这类项目的经验把它从头到尾拆给你看环境怎么配、源码怎么读、怎么跑起来、改了哪里会翻车。读完你能直接把这套货变成自己答辩时的东西而不是打开压缩包之后对着报错发呆。2. 这套东西到底跑在什么环境上JDK、Tomcat、数据库怎么配才不打架接手任何 JSP 老项目第一步不是打开了 IDE 就看代码是先把环境版本对齐。这类项目的代码普遍写在 2010 年前后当时的 JDK 是 1.6 或 1.7Tomcat 是 7 或 8MySQL 还是 5.x。你现在电脑上装的如果是 JDK 17 加 Tomcat 10把代码直接扔进去九成是跑不起来的。先把这条最关键的教训记住老项目最怕新环境不是代码有毛病是版本之间的约定变了。2.1 版本组合怎么选照着这三组选稳的我一般不会赌运气直接用最稳的组合。JDK 1.8 是个万金油Tomcat 8.5 能兼容大部分老代码MySQL 5.7 在 Windows 上装起来也方便而且网上驱动包和配置教程最多。这套组合的意思是JDK 8 提供运行环境Tomcat 8.5 当 Web 容器MySQL 5.7 存数据。如果你机器上同时装了多个 JDK要在环境变量JAVA_HOME里指定用哪一个。老项目对 JDK 9 以上的模块化改造很不友好所以我建议直接锁定 JDK 8# JDK 8 安装好后验证版本 java -version # 期望输出类似java version 1.8.0_202 # 设置 JAVA_HOMEWindows 在系统属性里配这里给 Linux/macOS 的写法 export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 export PATH$JAVA_HOME/bin:$PATH这段逻辑不复杂。JAVA_HOME指向 JDK 安装根目录Tomcat 启动脚本会去这个路径下找bin/java可执行文件。如果装的是 JDK 17java -version输出的就不是1.8.xTomcat 8.5 虽然能勉强跑但你在编译部署时遇到的不兼容报错会耗掉你一整天。参数就一个要点一定要确认版本输出里的主版本号是 1.8别的都不认。然后是 Tomcat。我见过有人为了追新直接上 Tomcat 10结果代码里javax.servlet.*导入全部找不到。Tomcat 10 把 Jakarta EE 的包名从javax.*换成了jakarta.*而老项目用的全是javax系列接口。这不是改个 import 就能解决的牵扯到的 jar 包和部署结构会全面崩塌。所以选 Tomcat 8.5 是这类项目的安全牌下面的配置方法也按这个版本写!-- Tomcat 8.5 默认监听 8080 端口在 conf/server.xml 里可以改 -- Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /这段配置在conf/server.xml中。端口为什么叫 8080这是 Tomcat 的约定端口不是必须的但如果你的 8080 被别的程序占用了比如自己跑了一个 SpringBoot 应用就要在这里改成 8081 或 8082。connectionTimeout20000是连接超时毫秒数老项目调试时如果频繁断连可以把 20000 调大到 30000但不是必要的。redirectPort是 SSL 重定向端口老代码不涉及 HTTPS不用管。2.2 MySQL 5.7 装完后字符集和密码是两件头等大事数据库版本我推荐 5.7原因很简单市面上大量 JSP 老项目的 SQL 脚本和 JDBC 驱动都针对这个版本调过。MySQL 8.x 虽然也能连但驱动类名和时区参数处理方式不同比如 8.x 的驱动类名变成了com.mysql.cj.jdbc.Driver老代码写死的是com.mysql.jdbc.Driver还要额外处理serverTimezone参数新手容易卡在这一步。装完 MySQL 5.7 后建库之前必须做一次配置检查。我用的是自定义配置文件来保证后续不乱码[mysqld] character-set-serverutf8 collation-serverutf8_general_ci default-storage-engineINNODB max_connections100把这段存成my.iniWindows或my.cnfLinux/macOS放在 MySQL 的安装根目录。character-set-serverutf8这一行最重要它保证建出来的库默认就是 UTF-8 编码不会出现中文水果名变成问号的情况。collation-serverutf8_general_ci是指定排序规则general_ci不区分大小写适合大多数管理系统。max_connections设 100 就够了一个成绩管理系统的并发远达不到这个数。设置完重启 MySQL 服务然后用一个 SQL 检查当前编码SHOW VARIABLES LIKE character_set%;这个命令输出会列出character_set_server、character_set_database等若干变量你只需要确认character_set_server的值是utf8不是latin1或utf8mb4。如果还是latin1说明配置文件没生效检查一下是不是放了多个 my.ini 导致加载了别的配置。接下来是密码问题。老项目的 JDBC 配置文件里常写着root/root这种默认组合所以你在建数据库用户时要么直接把 root 密码设为 root要么改成自己的密码后记得同步修改源码里的配置文件。我一般图省事本地环境直接用 root/root项目跑通了再说。但在真实项目里绝对不这么干只是本地演示闭环怎么快怎么来。2.3 把压缩包解压后先用五分钟看懂目录再上手解压后的结构通常包含源码目录、lw 论文和 ppt。用我习惯的方式打开源码你会发现一个典型的 JavaWeb 工程fruit-sales/ ├── src/ │ ├── com/fruit/dao/ # 数据访问层 │ ├── com/fruit/entity/ # 实体类 │ ├── com/fruit/servlet/ # Servlet 控制器 │ ├── com/fruit/service/ # 业务逻辑层 │ └── com/fruit/util/ # 工具类DB连接等 ├── WebContent/ │ ├── admin/ # 管理员后台页面 │ ├── css/ # 样式表 │ ├── images/ # 图片资源 │ ├── js/ # 前端脚本 │ ├── user/ # 用户前端页面 │ ├── index.jsp # 入口页 │ └── WEB-INF/web.xml # 核心配置文件 └── fruit.sql # 数据库初始化脚本这段目录结构是这类项目的典型骨架。src下是按包划分的 Java 源码WebContent是站点根目录所有 JSP 页面和静态资源都在这。WEB-INF/web.xml是整个 Web 应用的核心配置Servlet 映射、欢迎页、过滤器都在这里声明——老项目没有 Spring Boot 那样的自动装配一切靠这个 XML 撑起来。fruit.sql是数据库初始化脚本导入到 MySQL 就能拿到带测试数据的完整库。先确认工程结构再动手的好处是你知道改代码要往哪些目录走不会在后续配置时找不到文件。我一般最先打开的是src/com/fruit/util/DBUtil.java因为数据库连接全在这里如果连接串不对后面什么都不用谈。好环境这一关过完下一步我们来读源码看这套系统是怎么工作的。3. 源码解构从登录到下单水果销售系统的 JavaWeb 分层链路你现在有了环境接下来要做的事是把这套代码读进脑子里。很多人拿到源码直接搜“怎么运行”结果服务器起不来就慌了。我换个顺序先不启动纯读代码搞清楚数据怎么流转然后再去跑。这样出了问题你知道去哪里查日志而不是干瞪眼。3.1 数据库表设计五张表撑起一个水果商城老课程设计项目的 SQL 脚本一般不会玩复杂的存储过程或视图就是几个基础表外加测试数据。水果商城一般有用户表、商品表、类别表、购物车表、订单表有的项目还加订单明细表。我从脚本里抽出建表语句这一段必须看明白CREATE TABLE user ( USER_ID int(11) NOT NULL AUTO_INCREMENT, USER_NAME varchar(255) DEFAULT NULL, USER_PASSWORD varchar(255) DEFAULT NULL, USER_SEX varchar(10) DEFAULT NULL, USER_BIRTHDAY datetime DEFAULT NULL, USER_IDENTITY_CODE varchar(255) DEFAULT NULL, USER_EMAIL varchar(255) DEFAULT NULL, USER_MOBILE varchar(255) DEFAULT NULL, USER_ADDRESS varchar(255) DEFAULT NULL, USER_STATUS int(11) DEFAULT NULL, PRIMARY KEY (USER_ID) ) ENGINEInnoDB AUTO_INCREMENT3 DEFAULT CHARSETutf8;这张user表是典型的用户信息表。USER_ID是自增主键USER_NAME存用户名USER_PASSWORD存密码。注意这里密码是明文存储的——老项目的通病没有任何加密答辩时老师可能以此问“你的系统安全吗”你要提前想好怎么回答我后面单独讲。USER_STATUS是一个值得留意的字段一般是 0/1 表示是否禁用或是否管理员要看UserDao里的查询逻辑怎么用。商品表fruit或product的结构也差不多重点是FRUIT_IMAGE这类图片路径字段。很多新手卡在“图片不显示”这个问题上后面避坑章专门说。先记住图片路径存的是相对路径不是绝对路径更不是 base64 字符串。3.2 分层请求流程一个登录动作从 JSP 走到数据库再走回来这套系统用的是教科书式 MVC 写法JSP 当视图层Servlet 当控制器业务逻辑放在 service 包数据访问在 dao 包。我画给你一个登录请求的完整链路用一个伪代码展示它实际执行的过程——用伪代码是因为不同项目的代码风格有差异但流程一定一致浏览器 POST /userLogin - 服务器依据 web.xml 中 url-pattern 找到 LoginServlet.doPost() - servlet 调用 UserService.login(username, password) - UserService 调用 UserDao.findUserByUsername(username) - UserDao 使用 DBUtil.getConnection() 执行 SELECT * FROM user WHERE USER_NAME ? - 结果封装成 User 实体对象返回 - 回传到 servlet 判断密码是否一致 - 如果匹配session.setAttribute(user, user)重定向到 index.jsp - 不匹配request.setAttribute(msg, 用户名或密码错误)转发回 login.jsp这个流程你可以拿去答辩用——这就是 MVC 分层最直白的解释。关键是那个session.setAttribute(user, user)后续 JSP页面判断用户是否登录全靠 session 里有没有这个对象页面顶部显示“欢迎 xxx”也是从这里取的。理解了这个才能看懂为什么登录之后刷新一下页面又跳回登录页——session失效了。看一个真实存在的 Servlet 代码段风格是老项目最常见的WebServlet(/admin/orderList) public class OrderListServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 判断是否有管理员登录 Object admin request.getSession().getAttribute(admin); if (admin null) { response.sendRedirect(../adminLogin.jsp); return; } OrderService service new OrderService(); ListOrder orders service.findAllOrders(); request.setAttribute(orders, orders); request.getRequestDispatcher(/admin/orderList.jsp).forward(request, response); } }注意看WebServlet(/admin/orderList)——这是 Servlet 3.0 的注解写法如果你的 Tomcat 版本低于 7这个注解不生效只能在 web.xml 里手动配 Servlet 映射。老项目里有些类用注解有些用 web.xml两种方式混着来很常见。这代码的逻辑是先检查 session 里的 admin 对象没有就重定向回登录页这是一个粗糙的权限控制手段然后调用 service 层拿全部订单列表放到 request 里转发到 JSP 页面。forward和sendRedirect的区别我顺手提一句forward 是服务器内部转发浏览器 URL 不变sendRedirect 告诉浏览器重新请求一个新地址URL 会变。翻车点在于转发到 JSP 时 request 里的数据还能拿到重定向之后 request 会丢只能用 session 传参。3.3 购物车和下单模块答辩时最容易被追问购物车是这类系统的核心模块也是老师们最爱问的地方。老项目几乎不用 Cookie 存购物车而是用 session 来存实现方式一般是 Map 或一个 List// 加入购物车的核心逻辑 HttpSession session request.getSession(); // 从 session 里取出购物车 Mapkey 是商品IDvalue 是购买数量 MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMapInteger, Integer(); } // 如果商品已经在购物车中数量加一否则放入购物车 Integer fruitId Integer.parseInt(request.getParameter(fruitId)); if (cart.containsKey(fruitId)) { cart.put(fruitId, cart.get(fruitId) 1); } else { cart.put(fruitId, 1); } session.setAttribute(cart, cart);购物车以MapInteger, Integer的形式存在 session 里整数 key 是水果 IDvalue 是用户想要的数量。这里有两个隐患第一session 是用户会话级存储关掉浏览器再开就没了所以购物车数据不会持久化第二Map 的值只存了数量没存单价和图片——因此购物车页面展示时每次都要从数据库重新查水果信息这在老项目里是标准做法。如果你答辩时想体现改进意识可以提出“用数据库购物车表替代 session 购物车实现持久化保存”这是个加分项。下单模块一般分为两步先创建订单主表记录再逐个插入订单明细。代码可能是这样public boolean createOrder(Order order, MapInteger, Integer cartItems) { Connection conn null; PreparedStatement ps null; boolean success false; try { conn DBUtil.getConnection(); // 关闭自动提交保证订单头和明细在同一事务写入 conn.setAutoCommit(false); String insertOrder INSERT INTO orders (ORDER_NO, USER_ID, ORDER_TIME, ORDER_TOTAL_PRICE, ORDER_STATUS) VALUES (?, ?, ?, ?, ?); ps conn.prepareStatement(insertOrder, PreparedStatement.RETURN_GENERATED_KEYS); ps.setString(1, order.getOrderNo()); ps.setInt(2, order.getUserId()); ps.setTimestamp(3, order.getOrderTime()); ps.setDouble(4, order.getTotalPrice()); ps.setInt(5, 1); ps.executeUpdate(); // 拿到订单主表生成的自增ID再写明细 ResultSet rs ps.getGeneratedKeys(); int orderId 0; if (rs.next()) { orderId rs.getInt(1); } String insertDetail INSERT INTO order_detail (ORDER_ID, FRUIT_ID, FRUIT_NAME, PRICE, QUANTITY) VALUES (?, ?, ?, ?, ?); for (Integer fruitId : cartItems.keySet()) { ps conn.prepareStatement(insertDetail); ps.setInt(1, orderId); // 这里要查询水果信息填充明细 // ... ps.executeUpdate(); } conn.commit(); success true; } catch (Exception e) { rollbackQuietly(conn); throw new RuntimeException(订单保存失败, e); } finally { DBUtil.close(conn, ps, null); } return success; }这段代码里setAutoCommit(false)是重点它把所有 SQL 包在一个事务里要么全部成功要么全部回滚。这是订单模块必须做的事因为如果订单主表写入了但明细失败会出现“有订单没商品”的脏数据。PreparedStatement.RETURN_GENERATED_KEYS的作用是拿到 MySQL 自增主键这样主表生成一个ORDER_ID明细表才能引用它。很多照着敲代码的初学者忘了这一步明细表的外键就是空值——查不出订单项目来。回滚方法rollbackQuietly(conn)在老项目中常被写成各种名字但本质是同一个意思catch 到异常后调用conn.rollback()。这个细节在答辩时提一句“事务控制保证了一致性”老师基本不会再追问。3.4 DBUtil 里藏着所有连接秘密先改这里最后看一眼工具类。每个 JSP 老项目必有一个数据库连接工具类名字大概率是 DBUtil 或 DBHelper。这个类是所有模块跑通的前提也是你先下手改的地方public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/fruit?useUnicodetruecharacterEncodingutf-8; private static final String USERNAME root; private static final String PASSWORD root; public static Connection getConnection() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USERNAME, PASSWORD); } }这里的四个变量是这套系统能不能跑起来的关键DRIVER是 MySQL 驱动类名如果你用 MySQL 8.x 的驱动包后面要加.cjURL里最重要的部分是3306端口和fruit数据库名要和你的 MySQL 实际配置一致USERNAME和PASSWORD必须改成你的数据库账号。用Class.forName(DRIVER)是为了把驱动类加载进 JVM让DriverManager能找到对应的驱动来建立连接。改密码时注意这个类有可能被写成了静态块加载驱动也可能每次getConnection()里都写一遍Class.forName——两种写法功能一样你只要确保常量值正确。源码读到这里你已经掌握了它的整体运作方式现在可以动手把整套系统跑起来了。4. 本地跑起这套 JSP 水果系统从导入数据库到浏览器看到页面环境配好了源码也过了一眼现在是动真格的时候把系统从压缩包变成浏览器里能点击的页面。我给你的步骤是我自己验证过的最短路径每一步都有明确的目的。如果哪一步失败直接跳到最后一章避坑清单找原因。4.1 导入数据库运行 fruit.sql 脚本打开 MySQL 命令行或 Navicat执行下面的命令mysql -uroot -p # 输入密码后进入 mysql 命令行 # 创建数据库这一步之前可以先看 SQL 脚本里有没有 CREATE DATABASE CREATE DATABASE IF NOT EXISTS fruit DEFAULT CHARACTER SET utf8; USE fruit; SOURCE /path/to/fruit.sql;SOURCE是 mysql 命令行里的指令不是 SQL 语句它会按顺序执行一个文本文件里的所有 SQL。fruit.sql里通常包含建表语句和 INSERT 测试数据执行完你可以用SHOW TABLES;验证一下表是否齐全正常应该看到上面说过的五张表。执行 SQL 脚本时如果报错最常见的问题是字符集不对DEFAULT CHARACTER SET utf8的作用就是保证脚本里的中文注释和测试数据不会乱码。导入完成后检查一下水果表里有没有数据SELECT FRUIT_ID, FRUIT_NAME, PRICE FROM fruit;这条语句确认测试数据是否进了库。老项目的测试数据一般是“红富士苹果”“香蕉”之类有几行数据就行没有的话后面页面是空的。4.2 把工程导入 IDEEclipse 的经典姿势这类老项目的元工具是 Eclipse不是 IntelliJ IDEA。如果你用 IDEA导入时选“Eclipse”项目类型才能正确识别.classpath和.project文件。步骤是File - New - Project from Existing Sources然后选择源码根目录Type 选 Eclipse。如果你用 Eclipse 就简单了File - Import - Existing Projects into Workspace选择目录Finish。导入后要做三件事一是配置 Tomcat。在 Eclipse 的 Servers 视图里新建一个 Server类型选 Tomcat v8.5 Server然后指向你的 Tomcat 安装目录。二是把项目 Add 到这个 Server 上。三是检查 Build Path 里有没有 MySQL 驱动 jar 包——没有的话自己下载一个mysql-connector-java-5.1.x.jar放到 WebContent/WEB-INF/lib 下就行。这一步最容易翻车的点在于很多项目用 Maven 管理依赖但老课程设计项目几乎没有 Maven全靠手动把 jar 包塞进WEB-INF/lib。如果你发现项目里有pom.xml那就按 Maven 工程导入等依赖下载完再跑如果没有就按普通 Web 工程处理jar 包自己放。4.3 启动 Tomcat 并访问页面启动之前最后检查一遍DBUtil里的数据库密码然后右键项目Run As - Run on Server。启动日志里看到Server startup in [xxxx] ms就说明 Tomcat 起来了。在浏览器输入http://localhost:8080/fruit/index.jsp这里的fruit是 Context Path取决于你部署时的项目名。如果在 Eclipse 里部署时起的是别的名字URL 也要跟着变。看到首页正常渲染出水果列表说明整套系统已经跑通接下来的工作就是反复点击各个页面测试登录、加购物车、下单、后台管理这些功能。测试时要特别关注控制台的日志比如下单时如果报 NullPointerException 或 SQLException多半是 session 里没有登录用户或数据库连接配置有问题。跑通了所有流程你对这套系统的掌握就达到了答辩不慌的程度。但跑通只是第一步接下来我把这套系统里最容易让你翻车的地方一次性说完免得你到时候抓瞎。5. 避坑清单这五个问题解决了项目才真正属于你5.1 页面中文全部变成问号现象浏览器打开页面水果名称全部显示成???SQL 控制台输出的中文也是乱码。原因分两层一是 MySQL 字符集不是 UTF-8二是 JSP 页面响应编码不对。前者我们在配置文件里通过character-set-serverutf8解决后者是因为老项目 JSP 页面顶部没写pageEncoding或者写了ISO-8859-1。解决找到所有 JSP 页面把文件头改成% page languagejava contentTypetext/html; charsetutf-8 pageEncodingutf-8 %在 Eclipse 里可以用CtrlH全局搜索所有contentType统一替换charsetGBK或charsetISO-8859-1为charsetutf-8。改完刷新浏览器问题消失。5.2 Tomcat 启动时报 8080 端口被占用现象启动 Tomcat 时日志出现Port 8080 required by Tomcat v8.5 Server at localhost is already in use。原因本地有其他程序占用 8080最常见的是你已经跑过一个 Tomcat 实例没有关或者别的开发服务器占了端口。这个报错属于端口冲突和代码没关系。解决先把占用端口的进程揪出来。Windows 上在命令行执行netstat -ano | findstr :8080看到 LISTENING 状态的行最后一列是 PID然后开任务管理器找到那个 PID 的进程确认是残留的 Java 进程就结束它。如果你需要保留那个进程那就去conf/server.xml里把端口改成 8081同时把访问 URL 改成http://localhost:8081/fruit/index.jsp。这里面有个隐藏坑不只是 HTTP 端口Tomcat 的 shutdown 端口也可能冲突默认在 8005报错日志会写清楚按上面思路一并改掉即可。5.3 管理员登录成功但页面跳转报 404现象后台管理员账号密码输入正确点击登录后跳转到http://localhost:8080/fruit/admin/xxxx.jsp显示 404 页面。原因可能是web.xml里 Servlet 映射路径和实际 JSP 页面路径不匹配也可能是 JSP 文件根本不存在。老项目里经常出现代码和页面版本不一致的情况——压缩包里的源码被某位同学改过admin目录下的文件名和 servlet 转发的目标字符串对不上。解决打开浏览器开发者工具看 Network 标签页里的请求路径再对照WebContent/admin目录下的实际文件列表。如果确实是要跳转的 JSP 文件不存在就只能找到最接近的页面修改 servlet 里的转发路径。这种问题不是环境配出来的是项目本身残次处理时要冷静。5.4 图片不显示只有 alt 文本现象区块里所有水果图片都是空白右键查看图片地址是http://localhost:8080/fruit/images/apple.jpg但手动访问 404。原因老的开发环境里上传图片会写到磁盘绝对路径比如D:/upload/fruit/xxx.jpg而 JSP 页面里引用的路径是部署后的虚拟路径对不上。另一个原因是你换电脑后图片资源根本没拷进项目images目录是空的。解决先确认images目录里有没有对应的图片文件。没有的话找几张测试用水果图片命名匹配数据库里的图片路径字段放到WebContent/images下。有文件但页面还是 404就在 JSP 页面里把img srcimages/apple.jpg改成项目全路径${pageContext.request.contextPath}/images/apple.jpg。注意老项目可能用了%basePath%这种写法优先用工程师最常见的 contextPath 方案。5.5 MySQL 连接失败驱动类找不到或者密码不对现象点开任意一个需要查数据库的页面Tomcat 控制台报错ClassNotFoundException: com.mysql.jdbc.Driver或Access denied for user rootlocalhost。原因前者是驱动 jar 包没有部署到WEB-INF/lib后者是DBUtil里的密码和 MySQL 实际密码不一致。还有一种少见情况是 MySQL 5.7 默认只允许 localhost 连接你的 URL 写了127.0.0.1但本地认证方式不对也会连接失败。解决针对驱动缺失把mysql-connector-java-5.1.49.jar版本号自己选复制到WebContent/WEB-INF/lib然后在 IDE 里刷新项目。针对密码不对把DBUtil里的USERNAME和PASSWORD改成你自己的账号改完记得重启 Tomcat——Web 应用加载后 jar 包不会自动重新读取必须重启。如果 MySQL 报认证错误用命令行mysql -uroot -p能连上说明是代码里的密码错了不是 MySQL 的问题。这五个问题覆盖了我接手类似项目八成以上的失败现场。接下来最后一个方向我想跟你聊聊怎么把这套老系统变得更好用以及为什么我建议你做一处改动。6. 进阶技巧给 JSP 水果商城换一层“现代皮肤”和一点“安全补强”如果时间允许我强烈建议你不要拿着原始页面直接交——会被老师一眼看出是下载的。最划算的做法是改两处一是页面样式二是用户密码存储前者让项目看起来不一样后者让你在答辩时能接住“安全”问题。改样式最简单不用动后端。找到WebContent/css目录下项目的主样式文件用一个免费 CDN 引入 Bootstrap 或纯 CSS 框架把表格和按钮的 class 换掉。比如老系统的登录按钮可能是input typesubmit value登录你改成 Tailwind 风格的样式也不影响表单提交逻辑button typesubmit classw-full bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded 登录 /button改动后端代码最值得做的是把密码明文存储换成 MD5 加盐或 BCrypt。老系统的UserDao里通常是这样的 SQLString sql SELECT * FROM user WHERE USER_NAME ? AND USER_PASSWORD ?; ps.setString(2, password);这就是明文校验数据库里存什么输入框就比对什么。如果你换成一个哈希后再比对——比如登录时把用户输入的密码做MD5再查就有得讲了。我给你一个能直接嵌进现有代码的工具方法import java.security.MessageDigest; public class MD5Util { public static String md5(String input) throws Exception { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); } }调用方法是注册时把md5(password)存进数据库登录时把md5(输入)}拼进 SQL和库里比对这个值。注意一个坑老测试数据里的密码是明文比如admin你要在修改逻辑前把数据库里的密码统一改成md5(admin)的值否则老账号全登不进去。推荐先写一个 SQL 批量更新再改代码UPDATE user SET USER_PASSWORD MD5(USER_PASSWORD);这条 SQL 利用 MySQL 内置的MD5()函数把现有密码一次性更新。改完后你的登录逻辑代码也要同步不能再用明文比对。用 MD5 不是最安全但比明文强太多而且 MySQL 的 MD5 函数和 Java 的MessageDigest输出是一致的不会出现两边哈希值对不上的情况。这一步做完答辩时老师问“你系统的安全性怎么考虑”你就可以明确说自己做了摘要存储而非明文算是这套老项目里少数能拿到主动分的地方。我也要跟你说句实话MD5 本身已不够安全彩虹表一查就破。加盐才是正解比如md5(username password salt)但老项目表结构里没有盐字段改动成本上升性价比低——你用它应付课程设计可以真放到生产环境别这么做。除了安全性另一个加分方向是把session购物车替换成数据库购物车。这动作需要建一张cart表并在每次加入购物车时改成INSERT或UPDATE这张表逻辑说复杂不复杂说简单也不简单。我见过一些学生用这招拿到了不错的答辩成绩因为它能体现你对状态管理有理解。做法是新建一张购物车表字段至少包含CART_ID、USER_ID、FRUIT_ID、QUANTITY然后写一个CartDAO负责增删改查把CartServlet里的session读写逻辑全部替换成CartDAO调用。页面展示也从session.getAttribute(cart)改成service.findCartByUserId(userId)。这事情要做一两个小时但做完你对 JDBC、DAO 模式的理解会比写十次作业都扎实。最后一个建议和具体代码无关是习惯问题动手改之前先复制一份原始工程做备份。这种带源码 lw ppt 的压缩包本身就是别人的劳动成果你改坏了随时能退回原始版本。这也是我用这套方式做所有旧项目的习惯——先跑通、再通读、最后小改永远留一手后悔药。希望这整套流程能帮你顺顺当当把这门课过了也让你在答辩前比同组人多几分把握。本文还有配套的精品资源点击获取