JSP网上玩具销售系统全解析:从架构搭建到答辩避坑
2026/9/23 2:06:18 网站建设 项目流程

简介:一份基于JSP的网上玩具销售系统毕业设计资源包,面向Java Web方向的高校学生与开发者,可作为课程设计、毕业设计的完整参考,解决了从系统分析、数据库设计到前后端实现、项目报告撰写的多项痛点。压缩包共1059个文件、约23.2MB,类型以JSP页面、Java源码、SQL脚本、HTML/CSS/JavaScript前端文件和GIF/JPG图片素材为主,另含配置类文件、第三方编辑器组件与项目文档,便于导入开发环境运行和二次定制。整体技术侧重JSP+Servlet+JavaBean,数据库脚本提及MySQL,适合希望独立完成网上商城项目的初中级学习者掌握Java Web分层与业务逻辑。已有390人学习下载;资源内包含前台商品浏览、购物车、用户中心等核心流程,同时给出导航栏设计、会话与Cookie处理、支付接口思路等实现细节,对照源码和数据库脚本即可快速跑通项目,有效节省从零搭建的时间。

1. 基于JSP的网上玩具销售系统:毕业设计高频选题,先搞清楚这三点

基于JSP的网上玩具销售系统,是JavaWeb毕业设计里出现频率极高的题目。拿到压缩包后,很多人看到一堆JSP页面、Servlet类、数据库脚本,第一反应不是兴奋,而是不知道从哪下手。这套资源我拆过一遍,先说结论:它本质上是一个标准的JSP Model 2结构的电商demo,JSP负责展示页面,Servlet处理请求,JavaBean封装数据,MySQL当仓库。它能帮你把前端页面、后端逻辑、数据库这三年学的零碎东西串成一条线。适合正在做毕业设计、或者想系统补一遍JavaWeb基础的人。如果你已经在用Spring Boot做项目了,这篇文章能让你反过来理解JSP时代的底层逻辑。

2. 技术选型与架构:JSP Model 2 为什么能成为毕设标准答案

2.1 从题目反推技术栈:JSP 是评审老师最熟悉的“安全牌”

毕业设计的评分标准,往往不是谁的技术新,而是谁能把业务讲清楚。JSP+Servlet+JavaBean的Model 2结构,恰好是大多数高校JavaWeb课程的标准内容。老师不用看代码,光看结构图就知道你的系统是怎么跑的。这一点是Spring Boot、SSM这些框架比不了的——不是不能用,而是答辩时你需要花更多时间解释框架本身,而不是业务。

那为什么不建议直接用Spring Boot 2集成JSP呢?两个原因。第一,Spring Boot对JSP的支持很别扭,需要额外配置视图解析器,war包部署时还容易踩路径坑。第二,毕设源码包大多都是从旧项目改过来的,你非要迁移到Spring Boot,等于把数据库脚本、JSP页面里的EL表达式、自定义标签全重写一遍,工作量翻倍。用这份资源自带的JSP+Servlet,反而是最快的路径。

所以我的建议是:如果你对JavaWeb还不熟练,就不要动“重构到Spring Boot”的念头。先把JSP Model 2跑通,答辩时讲清楚“请求怎么进来、模型怎么处理、视图怎么渲染”这条链路,比什么都管用。资源里虽然也有jsp文件夹、service层这类中间产物,但骨架仍然是京东淘客那种老式分层,只要老师问起MVC,你能指着代码说出Model、View、Controller分别对应哪些文件,这一关就过了。

2.2 运行环境:SQL Server 2005 源脚本与 MySQL 的实际配置

项目描述里提到“数据库选用的是SQL Server 2005,服务器环境是IIS5.0或更高版本”,但压缩包文件名又写着“jsp651玩具购物商城mysql-qrp”。这两个信息不冲突,说明这是一套经历过迁移的毕设源码。原始版本可能跑在IIS+SQL Server上,后来有人把数据库脚本改成了MySQL,重新打包成这份资源。实际开发中,我一般建议直接用MySQL,原因很现实:Tomcat+MySQL+JDBC是JavaWeb毕设最经典的组合,环境好搭,教程多,出了问题网上随便一搜就有答案。

如果你坚持要用SQL Server 2005,那我提醒一句:JDBC驱动很旧,和Windows 10/11的兼容性不好,而且SQL Server 2005的安装包本身就不容易找到。所以除非老师明确要求,否则不要自找麻烦。MySQL 5.7是这份代码最舒服的运行环境,往下兼容5.5,往上到8.0需要注意时区和驱动版本问题,这点在第5章会细说。另外,IIS和Tomcat并不冲突,但在Windows上调试JSP,直接用Tomcat反而简单,IIS只是给那些非要套ASP环境的同学添乱的,建议别碰。

2.3 项目目录里的陌生文件:fckeditor、class_upload.asp 和 io.asp 到底是什么

解压后,你在根目录或WebRoot里看到的fckeditor文件夹、class_upload.asp、io.asp,不是病毒,也不是老师故意放的干扰项。fckeditor是老牌的富文本编辑器,很多老项目用它做商品描述、新闻公告的在线编辑。class_upload.asp和io.asp是FCKeditor的ASP服务端上传组件,只有在部署到IIS + ASP环境时才会生效。现在你的项目是JSP + Tomcat,这两个文件完全不会参与编译和运行。

但这里藏着一个坑:如果你把整个WebRoot目录原样丢到Tomcat里,Tomcat虽然不会编译.asp文件,但访问这些URL时可能返回403或者让浏览器直接下载。更关键的是,FCKeditor的JSP版本需要单独配置上传处理类,默认的ASP上传类是失效的。所以我的做法是:直接删除class_upload.asp和io.asp,或者把整个fckeditor换成较轻量的KindEditor。压缩包里还有fckeditor.afp、sample01.afp、sample02.afp这一堆.afp文件,看起来像临时数据或者样例脚本,不是Java可用的类,同样建议排除。config.ascx是ASP.NET的用户控件配置,跟JSP项目也没有关系,看到它不用慌,删除或忽视都可以。

2.4 核心数据表设计:玩具商品、会员、订单三张表的字段清单

无论是SQL Server还是MySQL,玩具商城系统的表结构都大同小异。常见的表有:玩具商品表(t_product)、用户表(t_user)、订单表(t_order)、订单明细表(t_order_item)。这里给一份我惯用的核心字段,你可以拿去跟资源里的SQL脚本对比看有没有缺失。

表名核心字段说明
t_productid, toy_name, category_id, price, stock, image_url, description, create_timecategory_id关联分类表,图片存相对路径
t_userid, username, password, phone, email, reg_timepassword建议存MD5摘要,不存明文
t_orderid, user_id, order_no, total_price, status, create_timestatus用字符串或枚举,0表示未支付,1表示已支付
t_order_itemid, order_id, toy_id, quantity, price单价快照,防止商品改价后订单金额出错

这四张表是命令行级别的结构。特别注意订单明细的price字段,它是下单那一刻的商品价格快照,关联查询时不要用t_product的当前价格去算历史订单金额,这是很多毕设项目里会被答辩老师追问的地方。会员表和订单表之间通过外键关联,但实际开发中我更推荐只用普通索引约束逻辑关系,不建物理外键,因为毕设的增删改查操作频繁,物理外键容易让删除商品的时候报“有订单关联”的错,影响演示流畅度。老项目里用户表通常叫admin,但玩具商城是C端系统,管理员表反而应该单独建,不要让普通用户和管理员混在一张表里。

2.5 架构分层:index.jsp、ProductServlet、ProductBean 之间怎么配合

这套项目的代码分层不算严格,但核心链路还是清晰的。以商品列表为例,浏览器地址栏输入product?action=list,请求先到达ProductServlet的doGet方法。doGet里通过action参数判断要调哪个业务方法,然后调用ProductDao查询数据库,结果放进List ,再用request.setAttribute传值,最后forward到product_list.jsp。JSP页面通过EL表达式${product.toyName}循环打印数据。

这个过程中,ProductBean是纯数据载体,属性名必须和数据库字段名对应得上。ProductDao和ProductService之间的边界,老项目往往比较模糊,有时候直接在Servlet里new一个Dao就开始查。答辩时如果老师较真分层,你最好自己补一句“这里为了减少过度设计,把Service合并到Dao层了”,大多数老师都能接受。真正需要突出的不是分层,而是你对每一个请求生命周期的理解。

3. 搭建与数据库配置:从解压到跑起来,最长半小时

3.1 解压后的资源清单:哪些文件该看,哪些别动

拿到压缩包“jsp651玩具购物商城mysql-qrp”后,正常解压会得到一套完整Eclipse工程。你重点关注这几个目录:src(Java源码)、WebRoot(或web,存放JSP页面、CSS、JS、配置文件)、数据库脚本文件夹(通常是.sql文件)。

目录/文件作用建议
src/com/xxx/servlet控制器Servlet逐个阅读,理解请求处理
src/com/xxx/beanJavaBean实体类对应数据库表字段
src/com/xxx/util数据库连接工具、分页类重点看DBUtil
WebRoot/WEB-INF/web.xml项目配置检查Servlet映射和欢迎页
WebRoot/header.jsp顶部导航修改站点名、菜单项
sql/*.sql数据库脚本放到MySQL里执行
fckeditor、.asp、.afp无用上传组件建议删除或忽略

一个很重要的工作是:当你打开Eclipse开始修改前,先复制一份原始工程做备份。因为这种老项目经常会因为编码不同步、文件缺失导致编译不过,没有后悔药可吃。你可以在压缩包上右键重命名,保留一份带_backup后缀的版本,然后解压第二份开始干活。我每次拆这类源码包都会先建一个README.txt,记录解压后第一版能成功运行的状态,后面改坏了直接回滚。

3.2 数据库建库与导入:从.sql脚本到MySQL 5.7

先确认MySQL服务已启动。打开命令行或使用Navicat,执行以下命令完成建库和数据导入:

mysql -uroot -p123456 -e "CREATE DATABASE toy_mall DEFAULT CHARSET utf8 COLLATE utf8_general_ci;" mysql -uroot -p123456 toy_mall < sql/toy_mall.sql

这里toy_mall是数据库名,sql/toy_mall.sql是资源包里的脚本路径。如果脚本里原本就有CREATE DATABASE语句,第一行可以省略。手动指定utf8编码是关键,很多中文乱码的根源就是这里没有显式设置字符集。如果脚本执行过程中报错,先检查脚本文件本身是什么编码——用Notepad++打开,如果右下角显示ANSI,先转成UTF-8再执行。

导入成功后,用这条命令验证数据表是否都建好:

USE toy_mall; SHOW TABLES;

常见的输出应该有十几张表,比如t_user、t_product、t_order、t_order_item等。如果你发现只有表结构没有数据,那一定是脚本里INSERT语句被注释了,或编码问题导致插入失败。这种情况我一般不建议手动补数据,可以写一个简单的SQL循环生成500条玩具商品记录,但要注意价格和销量字段的随机范围。玩具商品的图片字段也要处理,老项目里的image_url多半是空字符串,建议批量改成项目实际要用的占位图路径,否则页面上一片裂图。

3.3 数据库连接参数:从SQL Server到MySQL的迁移关键点

原项目若是SQL Server 2005,则数据库连接字符串用的是jdbc:sqlserver协议,驱动类名是com.microsoft.sqlserver.jdbc.SQLServerDriver。现在用MySQL,需要找到src/util/DBUtil.java(或类似类),把连接写到统一位置。

// DBUtil.java 核心片段 public class DBUtil { private static String url = "jdbc:mysql://localhost:3306/toy_mall?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"; private static String user = "root"; private static String password = "123456"; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, user, password); } }

这段代码里的驱动类名,如果你用的是MySQL 5.7及配套驱动,写com.mysql.jdbc.Driver没问题;如果换到MySQL 8.0,驱动类名要改成com.mysql.cj.jdbc.Driver,并且serverTimezone参数不能省。URL里的characterEncoding=utf8useSSL=false是持久化连接不乱码和跳过SSL验证的两个关键参数,漏掉任何一个,后面都会弹出一堆奇怪的警告。因为这套代码很老,推荐用5.1.49的驱动jar包,不折腾。

修改完连接参数后,还要把对应的JDBC驱动jar包放进WebRoot/WEB-INF/lib目录。资源包一般自带,但版本可能老旧。我建议删掉旧的mysql-connector-java-x.x.x.jar,去Maven仓库下载一个5.1.49版本,这个版本对MySQL 5.7兼容性最稳,不会出现时区报错。如果项目里还有连接池配置,比如DBCP或c3p0,注意配置文件里也有一份数据库账号密码,别只改了DBUtil忘了配置文件。

3.4 Tomcat 部署:Eclipse内置Tomcat与独立Tomcat的差异

很多同学在Eclipse里直接run on server,跑通了就以为交给老师也能跑。实际上评阅老师可能没有Eclipse,只装了Tomcat。所以在答辩前,最好把项目打成war包,放进独立Tomcat的webapps目录里。常见做法是:

# 在Tomcat webapps目录下启动,访问地址会自动带上war包名 cp toy_mall.war /opt/tomcat/webapps/ /opt/tomcat/bin/startup.sh

启动后浏览器访问http://localhost:8080/toy_mall/,如果能看到登录页或首页,说明部署成功。如果报404,先看Tomcat日志catalina.out里有没有Servlet初始化异常。这里要特别提醒:如果web.xml里配置了欢迎页index.jsp,但工程里文件名是login.jsp,就会出现访问根路径404的情况。需要修改web.xml里的welcome-file-list,把它指向实际存在的页面。

另外,MyEclipse或Eclipse导出的war包和IDEA导出的war包结构会有差别。IDEA默认会将编译结果放在out/artifacts目录,并且war包里可能没有WEB-INF/classes下的配置文件,导完之后要检查一下。部署时建议把war包名称设置成toy_mall,不要叫ROOT,否则访问路径会和课程设计报告里写的不一致。答辩老师会按照你演示时的URL来核对功能,这里别翻车。

4. 核心功能实战:导航栏、商品列表、购物车到个人中心

4.1 导航栏复用:JSP include 指令与登录状态判断

导航栏是每个页面都会出现的公共部分,老项目里最经典的做法是用<%@ include file="header.jsp" %>静态引入。这样做的好处是编译后只有一个Servlet,性能好。缺点也很明显:header.jsp一旦改动,整个应用都要重新编译。但在毕设体量下,这个缺点可以忽略。

<%-- header.jsp 片段 --%> <%@ page contentType="text/html;charset=UTF-8" %> <% Object userObj = session.getAttribute("loginUser"); %> <div class="navbar"> <a href="index.jsp">首页</a> <a href="product?action=list&category=1">益智玩具</a> <a href="product?action=list&category=2">早教玩具</a> <a href="cart.jsp">购物车</a> <% if (userObj == null) { %> <a href="login.jsp">请登录</a> <% } else { %> <a href="user?action=myinfo">个人中心</a> <a href="user?action=logout">退出</a> <% } %> </div>

这段代码的核心是用session里的loginUser判断登录状态。注意,瘦身版的判断直接放JSP里,适合小项目;如果按Model 2标准,应该在Servlet里把用户信息塞到request或session,然后JSP用EL表达式${sessionScope.loginUser != null}判断。这里用脚本片段更直白,答辩时跟老师解释也没问题。关键是导航栏中的链接要跟web.xml里的Servlet映射一致,比如product?action=list对应ProductServlet。如果映射配的是普通字符串匹配,请求路径大小写也要严格统一。

4.2 商品列表分页:JSTL 标签 + 分页参数的正确写法

玩具商城商品数量多,分页必不可少。常见的分页实现是基于limit关键字:LIMIT offset, pageSize。在ProductServlet的doGet方法里,通过request.getParameter("page")拿到当前页,然后查询总记录数,计算出总页数。这里给出一段简化的Servlet分页逻辑:

int pageSize = 12; int currentPage = Integer.parseInt(request.getParameter("page") == null ? "1" : request.getParameter("page")); String sql = "SELECT COUNT(*) FROM t_product"; // 通过JDBC执行查询得到 totalCount int totalPage = (totalCount + pageSize - 1) / pageSize; int offset = (currentPage - 1) * pageSize; String sqlList = "SELECT * FROM t_product ORDER BY id LIMIT ?,?"; // 设置参数 offset, pageSize,查询列表放入 request.setAttribute("productList", list) request.setAttribute("currentPage", currentPage); request.setAttribute("totalPage", totalPage); request.getRequestDispatcher("product_list.jsp").forward(request, response);

分页算法里(totalCount + pageSize - 1) / pageSize是向上取整,这样总记录数为0时也能算出totalPage为0,避免页面显示“第1页/共0页”的尴尬。LIMIT后面的两个参数必须都是int,注意不能直接在SQL里拼接page参数,否则有SQL注入风险。JSP页面上用JSTL的c:forEach遍历商品,用c:if判断是否有上一页/下一页。

如果你发现引入JSTL标签报错,多半是在WEB-INF/lib里缺了jstl.jar和standard.jar,或者web.xml被配置成了Servlet 3.0规范需要新的taglib引用。老项目多半用的是标准标签,新Tomcat下可能不兼容,这时候建议把JSP页面里的JSTL换成最简单的Java脚本循环,代码丑一点,但稳定。我之前见过一个项目,JSTL引用的taglib地址还是sun的旧域名,Tomcat 9下直接解析失败,换成https://java.sun.com/jsp/jstl/core也不行,最后干脆把<c:forEach>改成<% for %>,问题立刻消失。

4.3 购物车模块:用 Session 存一个 Map,为什么比存数据库好

玩具商城的购物车,毕设阶段不需要做到存数据库。最常见的方案是把购物车对象放进session,用商品id作为key,购买数量作为value。这样做的好处是用户在未登录状态下也能加购物车,登录后再把session里的购物车数据合并到数据库。不过大部分毕设只做了前者。

购物车类可以设计成这样的JavaBean:

public class Cart { private Map<Integer, Integer> items = new HashMap<Integer, Integer>(); private double totalPrice; public void add(int toyId, int quantity) { if (items.containsKey(toyId)) { items.put(toyId, items.get(toyId) + quantity); } else { items.put(toyId, quantity); } recalculate(); } public void remove(int toyId) { items.remove(toyId); recalculate(); } private void recalculate() { // 遍历 items,根据 t_product 价格计算新 totalPrice // 正常情况需要调用商品Dao获取价格,这里省略 } // getter/setter 省略 }

注意recalculate方法需要在商品数量变化时执行,否则总价不对。很多同学把totalPrice做成独立的setter方法,在Servlet里手动累加,结果一刷新页面就重复累计。正确做法是:购物车的totalPrice永远从items里实时推导,不要在add方法外手动改。另外,session里丢购物车是常见问题,原因多半是web.xml里session的timeout被设置成了负数或过短。Tomcat默认session超时30分钟,如果你在代码里改了session.setMaxInactiveInterval(负数),会导致session永远不过期,这在空调房内无所谓,但在答辩演示时可能因为长时间不操作被系统自动踢下线,影响体验。

4.4 个人中心信息展示:JSP + Servlet 回显流程与 EL 表达式

搜索“jsp个人信息展示页面”的同学,遇到最多的状况是:页面能打开,但用户名、手机号显示空白。原因不是查询失败,而是Servlet没有正确把用户对象放到request作用域,或者JSP用EL表达式时属性名拼写有误。个人中心的完整流程是这样:浏览器发起user?action=myinfo,UserServlet从session取出loginUser并查出最新资料,再转发到user_info.jsp。

User user = (User) session.getAttribute("loginUser"); User freshUser = userDao.findByUsername(user.getUsername()); request.setAttribute("userinfo", freshUser); request.getRequestDispatcher("user_info.jsp").forward(request, response);

JSP页面上使用EL表达式获取:

用户名:${userinfo.username} <br/> 手机号:${userinfo.phone} <br/> 注册时间:${userinfo.regTime} <br/>

这里有个新手必踩的坑:JavaBean里如果是regTime属性,EL会解析getRegTime()方法。如果你在jsp里写${userinfo.reg_time},就会拿不到值。我看到很多毕设代码里,数据库字段用下划线,Java属性用驼峰,容易搞混。建议统一把数据库字段的别名改成驼峰,或者在SQL里显式加别名:SELECT reg_time AS regTime FROM t_user。用setAttribute传值后,千万不要再用response.sendRedirect,否则重定向会丢掉request里的数据,页面还是空白。这也是为什么要求用forward转发而不是重定向。如果你在个人中心还要显示用户头像,记得先在Servlet里把头像文件读成Base64字符串再传给页面,否则直接写相对路径可能会因为目录层级不对而显示裂图。

4.5 商品搜索功能:模糊查询与关键字高亮

很多同学做完基础CRUD,觉得系统已经完整了,其实商品搜索是提升完成度最快的一个功能。在导航栏加一个输入框,提交到product?action=search&keyword=拼图。ProductServlet里这样处理:

String keyword = request.getParameter("keyword"); keyword = new String(keyword.getBytes("ISO-8859-1"), "UTF-8"); // 如果Tomcat已配置请求编码,可以直接使用 keyword String sql = "SELECT * FROM t_product WHERE toy_name LIKE ? OR description LIKE ?"; // PreStatement 设置参数为 "%" + keyword + "%" request.setAttribute("productList", list); request.getRequestDispatcher("product_list.jsp").forward(request, response);

注意keyword的编码转换。如果JSP页面的请求编码是UTF-8,Tomcat 8以上默认UTF-8,可以不用做getBytes("ISO-8859-1")这步。但老项目经常在Tomcat 7下运行,Get请求的查询串会按ISO-8859-1解码,这里必须转一下,否则中文关键字全是乱码,搜索直接失效。Page里显示结果时,可以用字符串替换把关键字加上红色背景,这是答辩时一个非常加分的细节体验。

5. JSP 玩具商城避坑指南:五个高频翻车现场与排查思路

5.1 中文乱码:页面、数据库、服务器三重编码不一致

现象:商品名称和用户名在页面上显示成问号,或者插入数据库后变成“???”。原因:项目里JSP页面的pageEncoding是ISO-8859-1,数据库却是utf8,Tomcat默认请求编码又不是UTF-8,三重不一致导致乱码。解决:统一三处编码。JSP页面首行设置为UTF-8;数据库连接URL中加useUnicode=true&characterEncoding=utf8;同时给CharacterEncodingFilter过滤器设置请求编码。

<filter> <filter-name>encoding</filter-name> <filter-class>com.toy.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter>

从排查顺序上讲,先在浏览器里打开页面源代码,看中文是直接在HTML中乱码,还是数据本身是问号。前者改JSP编码,后者改数据库连接。只要这三步做了,90%的乱码问题都能解决。还有5%发生在SQL脚本文件本身,脚本文件是ANSI编码而数据库是utf8,导入就乱。用Navicat导入SQL时,注意选择文件编码为UTF-8。

5.2 EL 表达式取不到值:JavaBean 属性命名和 getter 不匹配

现象:商品详情页的${product.toyName}输出为空,控制台也不报错。原因:JavaBean类里写的方法是getToy_name(),而属性名是toyName,EL表达式按JavaBean规范找getToyName(),找不到就返回null。解决:要么把属性改成toy_name并写getToy_name(),要么在数据库查询SQL里用别名统一驼峰。更保险的做法是,用IDE的source生成getter/setter,别手敲。另一个常见原因是JavaBean没有被实例化,session里的对象类型是Object,强制转换时用了错误包名。比如session存的是com.toy.bean.User,你转成com.toy.pojo.User,页面就拿不到值。

5.3 富文本编辑器加载异常:.afp 和 .asp 文件不是 JSP 能调用的服务端组件

现象:商品描述编辑功能打不开,浏览器控制台报fckeditor.js或fckeditor.afp相关的404/500。原因:前面说过,老项目里的class_upload.asp、io.asp是ASP服务端上传器,Tomcat不处理它,而且fckeditor.afp是编辑器产生的临时数据文件,不是可调用接口。解决:最省心的是把整个富文本编辑器从fckeditor换成KindEditor或UEditor的JSP版。如果老师不允许换组件,就把fckeditor目录下的connectors/asp整个删掉,改成jsp目录下的上传类,同时确保fckeditor.js的路径正确。还有一点,如果在Eclipse里看到.afp文件报错,直接右键排除构建路径,它们跟项目运行无关。删除这些杂文件前,最好先全局搜索一下哪个JSP引用了它们,避免页面报文件不存在。这套资源里没有前端页面依赖这些.asp文件,所以直接删除就好。

5.4 JDBC 驱动版本与 MySQL 8.0 的兼容问题:通讯链路异常和时区报错

现象:Tomcat启动正常,但一查数据库就报The server time zone value '???ú±ê׼ʱ¼ä' is unrecognized,或者Communications link failure。原因:MySQL 8.0及以上版本,JDBC驱动类和时区处理都变了。解决:降级到MySQL 5.7,配合mysql-connector-java-5.1.49,问题最少。如果非要用MySQL 8.0,按3.3节那样把驱动改为com.mysql.cj.jdbc.Driver,并在URL加上serverTimezone=Asia/ShanghaiallowPublicKeyRetrieval=true。注意,5.1.49驱动不支持MySQL 8.0的caching_sha2_password认证方式,数据库用户密码要改回mysql_native_password。这是我在一个商城项目里被卡了一晚上的地方,那时MySQL 8.0刚出,很多人不知道新认证机制和老驱动不兼容。

5.5 Tomcat 部署后 404:web.xml 里 Servlet 映射和欢迎页不一致

现象:直接用Tomcat访问项目根路径出现404,但直接访问index.jsp能打开;或者通过页面链接访问product?action=list返回404。原因:项目默认欢迎页index.jsp根本不在WebRoot根目录下,或ProductServlet映射写的是/ProductServlet,但前端链接用的是product。解决:在web.xml的servlet-mapping里把URL模式改成和前端完全一致的路径,然后确认welcome-file-list里的页面真实存在。如果是在Eclipse里run on server正常、打包war后404,还要检查war包结构,不要把WEB-INF目录放错层级。正确结构是war根目录下直接有index.jsp,而不是再套一层项目名的子目录。部署后如果你访问http://localhost:8080/toy_mall/返回的是Tomcat默认首页,那说明war包可能没解压成功,去webapps目录看下有没有同名文件夹,把旧的这个目录删除后重新启动Tomcat再试。

6. 答辩前常做的三个升级:让系统从“能跑”变成“能演示”

6.1 统一异常处理:替换掉散落在每个Servlet里的 try-catch

翻看代码,你会发现每个Servlet里都有一段差不多的try-catch,错误提示写的是“系统错误”。我习惯在中间层加一个BaseServlet,把异常统一抛给入口,然后由一个ErrorPageServlet接收,记录日志后跳转到友好提示页。这样演示时不容易在页面上看到一堆堆栈,答辩观感完全不同。实现也不复杂,写一个实现了HttpServlet的父类,在service方法里包一层try-catch,捕获后把异常信息放到request里,转发到error.jsp。具体报错细节可以放在页面源代码注释里,给导师检查时看,但界面上只显示“开小差了,请刷新重试”。

6.2 会话超时提醒:防止在演示时突然掉线

在web.xml里设置session超时时间为20分钟,并利用session的isNew属性在登录前做一个判断。常见做法是在header.jsp里加入一个JS定时器,每60秒请求一次当前用户状态,如果发现session已经失效,直接弹窗提示重新登录。这个细节能明显提升演示的流畅度。因为答辩现场的时间往往比较紧张,你可能在讲架构时忽略了页面操作,等回头演示下单时发现已经超时,重新登录一遍很尴尬。我在header.jsp里加了一段简单的fetch:

setInterval(function () { fetch("user?action=checkSession") .then(function (response) { return response.text(); }) .then(function (result) { if (result === "timeout") { alert("登录超时,请重新登录"); location.href = "login.jsp"; } }); }, 60000);

UserServlet里对应的checkSession逻辑很简单:判断session.getAttribute("loginUser")是否为null,为空则输出字符串timeout,不为空输出ok。这段代码用的是原生fetch,不依赖jQuery,老项目里也能直接跑。如果你用的Tomcat版本较老,不支持async,也不要紧,这里只是普通请求。

6.3 用过滤器统一登录拦截

给用户中心、购物车结算这些需要登录的URL加一个登录验证过滤器。注意排除login.jsp、register.jsp、静态资源路径。这样你不必在每个Servlet里写“if (user == null) { response.sendRedirect("login.jsp"); }”,代码干净得多。一个简洁的LoginFilter如下:

public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String uri = request.getRequestURI(); String ctx = request.getContextPath(); if (uri.endsWith("login.jsp") || uri.endsWith("register.jsp") || uri.contains("/css/") || uri.contains("/js/") || uri.endsWith(".jpg") || uri.endsWith(".png")) { chain.doFilter(request, response); return; } Object user = request.getSession().getAttribute("loginUser"); if (user == null) { response.sendRedirect(ctx + "/login.jsp"); } else { chain.doFilter(request, response); } } }

注意这里图片静态资源前缀和使用filter-mapping的url-pattern要匹配。如果filter配的是/*,一定要把静态资源路径排除干净,否则图片全部加载不出来。我第一次配这个过滤器时,忘了排除.css,结果整个页面裸奔,所有样式都没了。当时还以为是CSS文件的问题,折腾半天才发现是filter把请求拦住了。从那以后,我每次配好filter,都会先跑一遍项目根路径,再用浏览器开发者工具看Network请求是不是有静态资源被误伤。这套过滤器逻辑在答辩时也很容易讲,老师说“你怎么控制未登录用户访问订单页”,你直接把web.xml里的filter-mapping截图扔到PPT里就行。

从那以后,我每次改web.xml都会先把原有配置备份成.bak文件,再一行行加,加完用curl -I http://localhost:8080/toy_mall/验证根路径是否正常,接着用无痕浏览器窗口把登录、加购、下单、退出全流程走一遍。这套动作帮我避开了很多临时改配置导致的大翻车。希望帮到你。

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

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

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

立即咨询