☰
JSP水果销售管理网站:环境配置、源码解析与避坑指南
2026/10/6 14:37:50 网站建设 项目流程

简介:一份基于JSP技术实现的水果销售管理网站毕业设计资源,主要面向Java Web方向初学者、高校学生以及需要快速搭建电商类管理系统的开发者。从技术栈看,它涉及JSP动态页面、MySQL数据表设计及前后台交互逻辑,适合有一定Java基础、希望理解完整项目结构的人群。系统采用B/S结构与MySQL数据库,按管理员和用户两类角色划分功能:管理员端涵盖个人中心、用户管理、水果信息管理、水果类别管理、销售单位管理、销售分析、会员分析、系统管理和订单管理;用户前端包含首页、水果信息、新闻资讯、个人中心、后台跳转、购物车等入口。从前台选购、购物车提交,到后台订单处理及销售、会员分析,整体流程较完整,具备商业业务参考价值。压缩包大小约23.1MB,包含项目源码、毕业论文(lw)和答辩PPT,可支持从系统设计、编码实现、文档撰写到结题汇报的毕设全流程;已有51人学习/浏览,适合毕业设计、课程设计或Java Web项目练习中作为蓝本,也可基于源码进行二次开发与功能扩展。

1. 二手旧项目:这套 JSP 水果销售管理网站值不值得接手

如果你是计算机专业的学生,这会儿多半正被课程设计或毕业设计逼到墙角。搜了一圈发现,最不缺的就是这种带“源码+lw+ppt”三件套的 JSP 老项目。这套 JSP 水果销售管理网站,光看标题就知道是十年前的经典课程设计套路:一个基于 JSP + Servlet + MySQL 的管理系统,配上论文(lw)和答辩 PPT,打包成一个压缩包。它要解决的问题非常明确——用最传统的 Java Web 技术栈,把“水果商城”的购物流程做完整:用户注册登录、浏览水果、加购物车、下单,管理员管商品、管订单、管用户。适合谁?两类人:一类是只求交差、能跑能答辩的学生,另一类是刚学完 JavaWeb、想找一套完整代码把 MVC 分层看明白的初学者。但说实话,这种老项目的坑比你想的多:JDK 版本不对跑不起来、Tomcat 版本挑人、数据库密码写死在代码里、编码乱成一团——我见过太多人解压之后卡在第一步就放弃了。这篇文章就按我实际接手这类项目的经验,把它从头到尾拆给你看:环境怎么配、源码怎么读、怎么跑起来、改了哪里会翻车。读完你能直接把这套货变成自己答辩时的东西,而不是打开压缩包之后对着报错发呆。

2. 这套东西到底跑在什么环境上:JDK、Tomcat、数据库怎么配才不打架

接手任何 JSP 老项目,第一步不是打开了 IDE 就看代码,是先把环境版本对齐。这类项目的代码普遍写在 2010 年前后,当时的 JDK 是 1.6 或 1.7,Tomcat 是 7 或 8,MySQL 还是 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_HOME(Windows 在系统属性里配,这里给 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 17,java -version输出的就不是1.8.x,Tomcat 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 port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

这段配置在conf/server.xml中。端口为什么叫 8080?这是 Tomcat 的约定端口,不是必须的,但如果你的 8080 被别的程序占用了,比如自己跑了一个 SpringBoot 应用,就要在这里改成 8081 或 8082。connectionTimeout="20000"是连接超时毫秒数,老项目调试时如果频繁断连,可以把 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-server=utf8 collation-server=utf8_general_ci default-storage-engine=INNODB max_connections=100

把这段存成my.ini(Windows)或my.cnf(Linux/macOS),放在 MySQL 的安装根目录。character-set-server=utf8这一行最重要,它保证建出来的库默认就是 UTF-8 编码,不会出现中文水果名变成问号的情况。collation-server=utf8_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`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

这张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(); List<Order> 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 里取出购物车 Map,key 是商品ID,value 是购买数量 Map<Integer, Integer> cart = (Map<Integer, Integer>) session.getAttribute("cart"); if (cart == null) { cart = new HashMap<Integer, 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);

购物车以Map<Integer, Integer>的形式存在 session 里,整数 key 是水果 ID,value 是用户想要的数量。这里有两个隐患:第一,session 是用户会话级存储,关掉浏览器再开就没了,所以购物车数据不会持久化;第二,Map 的值只存了数量,没存单价和图片——因此购物车页面展示时每次都要从数据库重新查水果信息,这在老项目里是标准做法。如果你答辩时想体现改进意识,可以提出“用数据库购物车表替代 session 购物车,实现持久化保存”,这是个加分项。

下单模块一般分为两步:先创建订单主表记录,再逐个插入订单明细。代码可能是这样:

public boolean createOrder(Order order, Map<Integer, 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?useUnicode=true&characterEncoding=utf-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 的驱动包,后面要加.cj;URL里最重要的部分是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 把工程导入 IDE:Eclipse 的经典姿势

这类老项目的元工具是 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-server=utf8解决;后者是因为老项目 JSP 页面顶部没写pageEncoding,或者写了ISO-8859-1。

解决:找到所有 JSP 页面,把文件头改成:

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>

在 Eclipse 里可以用Ctrl+H全局搜索所有contentType,统一替换charset=GBK或charset=ISO-8859-1为charset=utf-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 src="images/apple.jpg">改成项目全路径${pageContext.request.contextPath}/images/apple.jpg。注意老项目可能用了<%=basePath%>这种写法,优先用工程师最常见的 contextPath 方案。

5.5 MySQL 连接失败:驱动类找不到或者密码不对

现象:点开任意一个需要查数据库的页面,Tomcat 控制台报错:ClassNotFoundException: com.mysql.jdbc.Driver,或Access denied for user 'root'@'localhost'。

原因:前者是驱动 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 type="submit" value="登录">,你改成 Tailwind 风格的样式也不影响表单提交逻辑:

<button type="submit" class="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded"> 登录 </button>

改动后端代码,最值得做的是把密码明文存储换成 MD5 加盐或 BCrypt。老系统的UserDao里通常是这样的 SQL:

String 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 的压缩包,本身就是别人的劳动成果,你改坏了随时能退回原始版本。这也是我用这套方式做所有旧项目的习惯——先跑通、再通读、最后小改,永远留一手后悔药。希望这整套流程能帮你顺顺当当把这门课过了,也让你在答辩前比同组人多几分把握。

本文还有配套的精品资源,点击获取

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

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

立即咨询