恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
JSP+Servlet+MySQL甜品电商系统:从部署到答辩的完整课设指南
首页
资讯中心
/
JSP+Servlet+MySQL甜品电商系统:从部署到答辩的完整课设指南
JSP+Servlet+MySQL甜品电商系统:从部署到答辩的完整课设指南
发布时间:2026/10/10 12:25:46
简介面向 Java Web 课程设计与毕业设计的蛋糕甜品店购物网站源码包基于 JSPServletMySQL 实现完整覆盖管理员端与用户端功能商品分类、热销新品、注册登录、浏览商品、购物车、订单提交、模拟支付以及订单管理、客户管理和类目管理等模块适合学习 JDBC、DAO 模式与 MVC 分层思想。源码已在本地编译运行验证评审分达 95 分以上随包附带使用文档和数据库 SQL 脚本支持 JDK1.8/1.9、Tomcat8/9并可在 Eclipse、MyEclipse、IDEA 中导入部署便于直接参考和二次开发。资源包共 221 个文件以 55 个 class、55 个 java、19 个 jsp 为核心代码另含 9 个 jar 依赖库、SQL 数据库脚本、XML 配置以及 JS/CSS/图片等前端资源压缩包整体 7.23MB目录结构清晰。已有 187 人浏览学习适合需要快速搭建可运行项目、理解 Servlet 业务分层与购物流程实现的 Java Web 学习者参考。1. 手写 JSP Servlet 甜品电商系统为什么这个老技术栈依然是高分课设首选如果你在找一份能真正跑起来、能讲清楚、能应付答辩的 Java Web 课设基于 JSP Servlet MySQL 的甜品蛋糕店购物网站是绕不开的经典选择。它不像 Spring Boot 全家桶那样把一切都封装成黑匣子也不像纯前端项目那样缺少后端业务逻辑它恰好站在“你能讲明白每一行代码”的黄金位置上。这份项目不仅包含了完整的源码和使用文档还配套了初始化数据库脚本适合用来完成课程设计、毕业设计也适合想把手写 Java Web 功底补扎实的开发者。很多同学选课题时有个普遍焦虑现在企业都在用微服务学 JSP 是不是在走回头路我的看法恰恰相反。Servlet 是 Java Web 的根JSP 是视图层最直观的渲染方案把这两者配合 MySQL 手写一遍购物流程你才能真正理解会话跟踪、请求转发、事务边界和 SQL 拼接到底是怎么回事。带过不少同学做类似项目凡是能独立把这个甜品店购物网站写出来的后面换 Spring Boot 几乎只需要学注解和自动配置不会有任何断层感。这篇文章我会从技术选型、数据库设计、本地部署、核心模块代码、踩坑排查到性能优化完整拆解一条从拿到源码到能答辩演示的路径。每一段都会给出可以直接抄走的配置和代码同时也会把那些不写在文档里的坑点提前告诉你。2. 为什么选 JSP Servlet MySQL这套组合的真实定位与运行逻辑2.1 三个组件各自负责什么以及它们如何串联一次购物请求JSP、Servlet、MySQL 三者各司其职。Servlet 是控制器负责接收浏览器发来的 HTTP 请求解析参数调用业务方法然后决定跳转到哪个页面JSP 是视图负责把 Java 对象里的数据渲染成 HTML 展示给用户MySQL 是数据存储层负责把商品、用户、订单、购物车这些业务数据持久化到磁盘。三者通过一个 Web 容器串联起来实际开发中我用的是 Tomcat 8.5 配合 JDK 8这是这套技术栈最成熟稳定的搭配之一。一次完整的加购流程是这样的用户在商品列表页点击“加入购物车”浏览器向服务器发送一个携带商品 ID 和数量的请求Tomcat 根据 web.xml 里的映射规则找到对应的 ServletServlet 调用 Service 层代码Service 再调用 DAO 层通过 JDBC 操作 MySQL操作完成后Servlet 把结果写入 session 或 request再利用转发或重定向把请求交给 JSP 渲染响应页面。整个链路里Servlet 不直接写 HTMLJSP 不直接连数据库保持分层清晰。强调一个容易搞混的地方JSP 在第一次被访问时会由容器翻译成 Servlet 源码再编译成 Class 执行。所以 JSP 本质上还是 Servlet只是它更擅长写模板。这也意味着如果你在 JSP 里写了大量 Java 脚本片段% % 满天飞性能和维护性都不会好。在这个项目中JSP 页面只用 JSTL 和 EL 表达式展示数据业务逻辑全部收拢到 Servlet 里这也是答辩时老师比较看重的代码规范。2.2 相比 Spring Boot 和纯前端方案这套选型的优势和劣势选型评估不能只看技术新旧要看项目目标。对于课设和毕设评分维度通常是代码完整性、可讲解程度和运行效果JSP Servlet 在这种场景里有几个天然优势。第一代码可读性极强控制器、业务、数据访问对应 web.xml、Service 和 DAO 包结构一目了然第二不依赖复杂框架出现问题时你能用 debug 跟到每一行代码第三对电脑配置要求很低不需要配 Maven 私服、不需要下载几百兆的依赖一个 Tomcat、一个 JDK、一个 MySQL 就能跑起来。劣势也很明显。这套技术栈没有 Spring 的依赖注入对象之间的连接靠手动 new没有 MyBatis 这样的 ORMSQL 需要自己写稍不注意就会出现连接泄漏JSP 页面也不会像 Vue 那样做组件化复用页头页尾只能靠 include 指令或标签文件解决。实际项目里这套架构撑到几千用户量级没问题再往上就需要引入缓存和集群了。另外一种是当前流行的纯前端 后端 API 方案比如 Vue 或者 React 搭配 Node.js 或者 Spring Boot视觉效果确实更现代但课设答辩时老师大概率会追问“你这个请求是怎么从浏览器到数据库的”如果对 HTTP 协议和会话机制理解不到位纯前端项目反而更容易露怯。相比之下JSP Servlet 的思路足够直白适合把基本功展示清楚。2.3 Servlet 三种会话跟踪机制为什么购物车必须放 session购物网站的核心交互是购物车这就涉及一个经典问题HTTP 是无状态的服务器怎么知道当前请求来自哪个用户这个项目采用 session 保存购物车数据需要先理解 Servlet 规范的会话机制。常见方式有三种URL 重写、隐藏表单域、Cookie 与 Session 结合。URL 重写是把会话 ID 拼在链接后面缺点是 URL 会变得很长而且用户复制链接容易丢失会话隐藏表单域只能用于表单提交场景无法处理普通链接跳转Cookie Session 是主流方式服务器创建 session 对象时生成一个 JSESSIONID通过 Cookie 写入浏览器浏览器后续请求会自动携带这个 ID服务器据此找到对应的 session。购物车放在 session 里有两个直接好处一是购物车不需要持久化到数据库用户关掉浏览器再打开重新登录即可没必要为临时数据建表二是 session 是线程隔离的不同用户访问时拿到的是各自独立的购物车对象不会互相覆盖。要注意的是session 默认有效时间是 30 分钟如果用户把商品加到购物车后长时间不结算过期的 session 可能导致购物车数据丢失。所以我在 web.xml 里会把 session 超时调到 60 分钟同时在下单前做会话有效性检查。这里有个常见误解把购物车放在 application 上下文里就能实现“所有用户共享购物车”这绝对不行application 是全局共享的A 用户加购的商品 B 用户也能看到这属于典型的翻车操作。本项目的会话边界是用户信息放 session购物车放 session商品和订单存 MySQL全局配置参数放 application。2.4 什么是“高分项目”该有的代码结构包命名与分层规范拿到一份项目最先看的往往是源码的组织方式。这个甜品店购物网站采用标准的 MVC 分层包结构大致如下实体类放在 entity 包对应数据库表结构数据访问对象放在 dao 包负责 JDBC 操作业务逻辑放在 service 包处理事务和业务规则控制器放在 servlet 包负责请求分发过滤器放在 filter 包处理编码和登录验证工具类放在 util 包比如数据库连接池工具和字符串处理工具。JSP 页面按功能分目录前台用户页面与后台管理页面分开存放。这样的分包方式在答辩时非常好讲。老师问“订单模块在哪里”你直接说 service 包下的 OrderService 和 servlet 包下的 OrderServlet问“数据库连接怎么管理”你指向 util 包里的 JDBC 工具类。反过来如果所有类都塞在一个包里JSP 和 Servlet 混在一起代码就很难给出结构性解释。我在审代码时一般按三条标准快速判断项目质量是否使用预编译语句防止 SQL 注入是否有统一的编码过滤器事务是否收在 Service 层而数据访问层是否有连接回收。这三条同时满足项目质量基本就在合格线以上。3. 数据库设计与权限模型先把表结构和角色边界立住3.1 核心表结构拆解用户表、商品表、订单表和购物车表的字段设计先看用户表字段包含用户 ID、用户名、密码、手机号、邮箱、角色标识、注册时间和状态。密码字段不建议存明文本项目采用 MD5 加盐的方式存储盐值可以是用户名或固定字符串具体做法后续代码章节说明。角色标识用整数表示简洁高效比字符串角色名更适合代码判断权限过滤器里只需比较数值即可。商品表是前台的展示基础字段包含商品 ID、名称、分类 ID、价格、原价、库存、封面图路径、描述、上架状态、创建时间。价格字段建议使用 DECIMAL(10,2)不要用 FLOAT否则计算总额会出现精度丢失。库存字段使用 INT并增加库存下限校验。商品图片建议只存相对路径比如 /upload/goods/xxx.jpg不要存完整网络地址换环境后路径调整成本更低。订单表和订单明细表是典型的主从结构。订单主表包含订单号、用户 ID、总金额、收货人、联系电话、收货地址、订单状态、创建时间、支付时间、发货时间。订单号建议手动生成格式可用时间戳加用户 ID 后四位加随机数避免使用数据库自增 ID 对外暴露订单量。订单明细表包含明细 ID、订单号、商品 ID、商品名称、商品单价、购买数量、小计金额。这两个表通过订单号关联保证一次下单操作的业务数据完整落库与下方购物车表无关。购物车表有两种常见设计一种是完全基于 session 的临时结构不放数据库另一种是持久化设计字段包含购物车 ID、用户 ID、商品 ID、数量、加入时间。本项目两种能力都做了游客未登录时可使用 session 临时购物车登录后点击“合并购物车”将 session 库存入数据库表这样的好处是用户换设备登录后购物车不丢也便于在管理后台分析加购数据。如果你只想保留一种优先选择持久化方案对演示更有利。3.2 初始化 SQL 脚本的阅读顺序与验证方法项目附带一份数据库初始化脚本拿到后建议按以下顺序执行先创建数据库再切到该库然后建表最后插入基础数据。不要一上来就全选执行如果表之间存在外键关系或者插入顺序颠倒会出现建表失败或外键约束报错。常见的正确做法是先用浏览器或命令行客户端登录 MySQL执行建库语句后再依次执行建表脚本。脚本里通常包含管理员账号和测试商品数据这些数据是演示时必须有的否则后台管理页面会一片空白。执行完毕后做三件事验证第一步运行 SHOW TABLES 确认所有表都存在数量与你预期一致第二步运行 SELECT * FROM user 确认管理员账号存在并记录下来方便后面登录第三步运行 SELECT COUNT(*) FROM goods 确认商品数据条数再打开商品表看图片路径字段确认图片名称与项目上传目录里的文件能对上。这三个验证步骤能筛掉一半的“项目跑不起来”问题。一个很容易被忽略的细节是数据库字符集。建库语句必须显式指定 utf8mb4否则插入中文商品名会出现乱码。utf8mb4 兼容 utf8还能存 emoji适合现代网页环境。如果你看到的建库语句是 latin1 或没有指定字符集建议重新建库不要等数据插进去了再改改字符集的成本远高于重建。3.3 用户与管理员双身份控制数据库字段 过滤器双重路由本项目区分普通用户和管理员但用户表只有一张通过 role 字段区分。role1 表示管理员role0 表示普通用户。这样的设计比建两张用户表更合理因为登录逻辑可以共用只是登录成功后跳转的页面不同。管理员账号一般在初始化脚本里插入密码默认是 123456 或 admin123正式使用时应立即修改。权限控制的实现思路是定义一个登录过滤器拦截所有需要登录才能访问的路径再定义一个管理员过滤器拦截所有后台管理路径。登录过滤器校验 session 里是否存在 user 对象不存在则重定向到登录页管理员过滤器在登录校验通过后再校验 role 字段不是管理员则跳转到前台首页并提示无权限。两层过滤可以合并到一个过滤器里但分开写更容易理解和扩展。另一个细节是后台页面的静态资源路径。所有后台 JSP 页面建议统一放在 /WEB-INF/admin 目录下这样外部浏览器无法直接访问只能通过 Servlet 内部转发访问进一步封堵跳过过滤器直接请求后台页面的可能。前端页面则放在 WebContent 根目录下因为商品详情页和列表页需要允许未登录用户访问。4. 部署与运行把服务真实跑起来的最小步骤4.1 环境要求速查表与版本搭配建议部署这套项目对环境的要求不高但版本搭配有讲究。JDK 8 是兼容性最稳妥的选择配 Tomcat 8.5 完全没问题MySQL 5.7 或 8.0 均可连接驱动版本必须与 MySQL 版本对应。如果你本机装的是 MySQL 8.0驱动请使用 mysql-connector-java 8.0.x否则会报认证插件错误。Web 容器推荐解压版 Tomcat不需要安装配置环境变量后就能启动。还需要确认 IDE 的使用方式。使用 Eclipse 或 IDEA 均可IDEA 社区版和旗舰版都能完成部署。关键是 Tomcat 集成方式IDEA 里配置 Tomcat Server 时要选择 Local 模式指定 Tomcat 安装目录项目的 Artifact 要选择 war exploded 模式这样改完 JSP 不用重启容器浏览器刷新就能看到效果调试效率高很多。下面给出一个环境搭配速查表组件推荐版本说明JDK1.8过低版本不支持新语法过高版本需额外配置Tomcat8.5与 JDK8 搭配最稳定MySQL5.7 / 8.0驱动版本需对应连接驱动5.1.49 / 8.0.33看数据库版本选浏览器Chrome / Edge建议开启开发者工具看网络请求4.2 导入项目结构与配置数据库连接的核心参数拿到源码后先用 IDE 以项目方式导入而不是直接打开文件夹。IDEA 中选择 Open定位到项目根目录识别为 Web 项目。导入完成后先检查项目结构src 目录下应有 Java 源码web 目录或 WebContent 目录下应有 JSP 和 web.xml。如果 JSP 放在 WEB-INF 外面Tomcat 可以访问到放在 WEB-INF 内部则只能通过转发访问这点后续避坑章节细讲。数据库连接配置一般在 util 包里的 JDBC 工具类中集中管理本项目使用的是 C3P0 连接池配置集中在 c3p0-config.xml 文件里。核心参数包括数据库 URL、用户名、密码、初始连接数和最大连接数。连接地址的格式是 jdbc:mysql://localhost:3306/数据库名?useUnicodetruecharacterEncodingutf8如果不带字符集参数中文插入时大概率乱码。需要特别确认 URL 末尾是否追加了 useSSLfalse 参数本地连接可以省去 SSL 握手提升响应速度。配置文件中密码是明文这在课设演示时无伤大雅但你要知道这个风险点。演示前务必保证 MySQL 的 root 账号密码与配置一致如果本机 MySQL 密码含特殊字符注意配置里是否需要转义。最稳妥的方式是单独创建一个数据库用户而不是直接用 root这也是答辩时加了印象分的细节。具体配置模板如下!-- c3p0-config.xml 中的连接配置 -- property namejdbcUrl jdbc:mysql://localhost:3306/cake_shop?useUnicodetrueamp;characterEncodingutf8amp;useSSLfalse /property property nameuserdb_user/property property namepassword你的密码/property property nameinitialPoolSize5/property property namemaxPoolSize20/property参数说明useUnicode 与 characterEncoding 必须同时出现确保中文能正确存入数据库useSSLfalse 是本地开发时禁用 SSL 握手避免警告日志刷屏initialPoolSize 建议为 5maxPoolSize 在课设场景设为 20 足够过大会浪费数据库连接资源。如果配置后连接失败优先检查 MySQL 服务是否启动、账号密码是否正确、数据库名是否存在。4.3 部署到本机 Tomcat 并验证三层访问在 IDEA 里配置好 Tomcat 后点击运行按钮即可启动容器。第一次运行时建议直接看控制台日志重点关注有没有抛出 ClassNotFound 异常和数据源初始化失败的提示。启动成功后浏览器访问 http://localhost:8080/项目名/ 如果能看到首页说明基础部署成功如果 404优先检查访问路径中的项目名是否与 context path 一致以及首页文件名是否为 index.jsp。接下来按三层验证访问链路。第一层是用户端注册一个新账号然后登录登录后浏览商品列表把商品加入购物车进入结算页填地址提交订单。第二层是管理端用管理员账号登录进入后台商品管理页尝试新增一件商品再到订单管理页查看刚才提交的订单状态。第三层是数据库在 MySQL 客户端查询订单表订单是否写入查询库存表库存是否减少确认整个事务已经提交。这套验证覆盖了用户、商品、订单、库存、购物车、权限六大模块能通过说明项目核心链路完整。如果中途有一步失败按层定位前端报错看浏览器 Network 面板和后端控制台堆栈后台操作无效优先检查权限过滤器的拦截范围数据不一致优先检查 Service 层事务注解或手动事务边界。5. 核心模块实战实现注册登录、购物车与订单是关键路径5.1 注册与登录的服务端双重校验正则表达式与预编译 SQL注册模块虽然功能简单却是最容易暴露代码质量问题的地方。服务端必须对用户名、密码、手机号、邮箱做校验不能只依赖前端 JS 校验否则只要绕过浏览器直接发 POST 请求非法数据就能进入数据库。常见做法是在 Servlet 层使用正则表达式快速校验格式格式不合法直接回写提示并返回注册页不再执行后续数据库插入。密码不能明文存储我在代码中对密码采用 MD5 盐值加密。盐值选择用户名加固定串的组合比如 md5(userName cake_salt)这样即使两个用户密码相同密文也不同。需要注意 MD5 本身不是绝对安全但在课设和一般教学项目里已经足够实现简单且能回答老师关于密码安全的问题。注册成功后同时把 user 对象写入 session实现注册即登录省去用户二次登录的重复操作。注册的 DAO 层实现必须使用 PreparedStatement禁止通过字符串拼接 SQL。这是防 SQL 注入的第一道防线。字符串拼接带来的注入风险在登录接口最明显经典入参如 or 11 能直接绕过密码校验。使用预编译语句后参数会被当作数据处理不会改变 SQL 结构。代码实现如下// UserDao 中的注册插入方法 public boolean insertUser(User user) { String sql INSERT INTO user (username, password, phone, email, role, create_time) VALUES (?, ?, ?, ?, ?, NOW()); try (Connection conn JDBCUtils.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, user.getUsername()); ps.setString(2, user.getPassword()); // 此处传入的是已加盐加密后的密文 ps.setString(3, user.getPhone()); ps.setString(4, user.getEmail()); ps.setInt(5, user.getRole()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }逻辑说明try-with-resources 保证连接和语句自动关闭避免连接泄漏占位符 ? 与 setString/setInt 一一对应防止拼接注入盐值加密应在 Servlet 或 Service 层完成DAO 层只接收处理好的密文职责更清晰。这里有个血泪经验getConnection 每次请求都调用如果连接池配置错误或者关闭不及时服务运行一小时后会出现连接耗尽页面卡死报“无法获取连接”所以 DAO 层里连接关闭顺序必须是先关闭结果集、再关闭语句、最后关闭连接。登录的逻辑要注意区分“用户不存在”和“密码错误”两种情况。常见做法是先按用户名查询用户查不到则回复“用户名不存在”查到了再比对密码。比对时把收到的密码做相同盐值加密后与数据库密文比较。如果不一致不要直接说密码错误统一提示“用户名或密码错误”这符合安全规范防止攻击者通过提示差异逐个试探有效用户名。登录成功后将用户 ID、用户名、角色存入 session后面所有需要用户信息的操作都从这里取。5.2 购物车的会话实现与合并策略从临时态到持久态购物车是本项目的高频操作对象。常见做法是用 MapInteger, Integer 存储商品 ID 与数量的对应关系外层再套一个 List 来保存商品详情快照。这里有一个要提前想清楚的选择购物车到底存商品 ID 还是存商品完整信息如果只存 ID每次展示购物车都查询数据库好处是价格和名称永远最新坏处是数据库压力大如果存完整信息用户把商品加购后又降价结算时页面显示的是旧价格所以下单时必须重新查库以数据库价格为准。访客未登录时购物车放 session登录后系统提供“合并购物车”操作把 session 里的临时购物车逐条检查数据库如果已有同一商品则数量累加没有则新增一条。合并完成后清空 session 中的购物车后续操作全部读取数据库购物车表。这个设计能应付演示中“先逛逛再登录结算”的完整场景。购物车数量变更涉及边界判断加购数量不能超过库存结算时再次校验库存防止库存不足仍然下单。这个校验不能只在页面做Service 层必须再次查询库存如果发现库存不足要直接抛异常阻止订单生成。下面给出购物车加购的 Servlet 核心逻辑// 加购请求处理 String goodsId request.getParameter(goodsId); int quantity Integer.parseInt(request.getParameter(quantity)); if (quantity 0 || quantity 99) { response.sendRedirect(cart.jsp?errorquantity); // 数量越界直接打回 return; } User user (User) session.getAttribute(user); if (user null) { session.setAttribute(cart, buildCartWithItem(goodsId, quantity)); // 游客走 session } else { cartService.mergeToDatabase(user.getId(), goodsId, quantity); // 登录用户走 DB } response.sendRedirect(cart.jsp);参数说明quantity 上限设为 99 是为了防止恶意大数量请求导致后续金额溢出用户未登录时商品加到 session登录时自动合并用重定向而非转发是避免刷新页面时重复提交加购请求导致数量翻倍。合并到数据库后需返回合并结果给页面提示例如“购物车中已有该商品数量已更新”而不是每次都追加新条目。5.3 库存扣减的事务边界与并发控制用 SELECT FOR UPDATE 锁行下单模块最核心的逻辑在于库存扣减与订单创建的事务一致性。一个常见的坏代码是先查库存判断库存足够然后减库存最后创建订单这四个操作之间没有任何加锁控制。在单用户演示场景下没有问题但如果演示时同时打开两个浏览器同一商品分别下单两个请求会同时读到库存 10各自扣减后写回 9最终库存变成 9 而不是 8这就是超卖。老师如果在答辩时提出并发问题回答不好会很减分。正确的做法是在数据库层面引入行级锁。实现方式是在事务内先执行 SELECT stock FROM goods WHERE id? FOR UPDATE这一步会锁住该商品行其他事务对同行的更新会进入等待拿到锁后再判断库存足够则执行 UPDATE 扣减并插入订单。锁在事务提交或回滚后自动释放所以必须确保整个流程处于同一个事务中。事务边界要放在 Service 层而非 DAO 层。一个简洁的做法是在 Service 方法中获取连接设置手动提交所有 DAO 操作传同一个连接执行完毕统一提交异常时回滚。框架项目可以用注解声明式管理事务手写 Servlet 项目则需要手动管理代码实现如下// OrderService 中的下单事务管理 public boolean createOrder(Order order, ListOrderItem items) { Connection conn JDBCUtils.getConnection(); try { conn.setAutoCommit(false); // 开启事务必须显式关闭自动提交 // 1. 锁定库存行 int stock orderDao.selectStockForUpdate(conn, order.getGoodsId()); if (stock order.getQuantity()) { conn.rollback(); return false; } // 2. 扣减库存 orderDao.reduceStock(conn, order.getGoodsId(), order.getQuantity()); // 3. 插入订单主表 orderDao.insertOrder(conn, order); // 4. 插入订单明细表 orderDao.insertOrderItems(conn, order.getOrderNo(), items); conn.commit(); // 全部成功提交事务 return true; } catch (SQLException e) { conn.rollback(); // 任一环节失败整体回滚 throw new RuntimeException(下单失败库存操作回滚, e); } finally { JDBCUtils.close(conn); // 确保连接归还连接池 } }参数说明selectStockForUpdate 必须在事务内先于扣减执行锁等待时间由数据库 innodb_lock_wait_timeout 控制默认 50 秒高并发时可调低但课程设计无需调整reduceStock 的 UPDATE 语句建议追加条件“WHERE stock 扣减数量”作为数据库层面的二次防线insertOrder 与 insertOrderItems 必须使用同一个连接跨连接无法共享事务。失败回滚后要告知用户“库存不足”而不是让页面停在 500 错误页。5.4 请求编码、乱码拦截与日志辅助定位中文乱码问题在这个技术栈里非常常见几乎每个项目都会遇到。乱码有两个来源请求参数乱码和响应输出乱码。GET 请求参数会先经过 Tomcat 的 URI 编码解析如果不设置 URIEncoding中文参数会显示为 % 编码后的乱码POST 请求参数的编码由 request.setCharacterEncoding(UTF-8) 决定必须在读取任何参数之前调用。响应输出乱码需要通过 response.setContentType(text/html;charsetUTF-8) 或 JSP 页面顶部的 page 指令配合解决。最省心的做法是写一个全局编码过滤器拦截所有请求强制执行 UTF-8。具体来说在 doFilter 方法中先调用 request.setCharacterEncoding(UTF-8)再调用 response.setContentType(text/html;charsetUTF-8)最后 chain.doFilter 放行。同时还要在 Tomcat 的 server.xml 里给 Connector 增加 URIEncodingUTF-8这样 GET 请求也能正确解析中文。日志排障方面课设项目不需要上 ELK 这类重量级方案但至少要保证后端控制台能输出关键操作日志。推荐在项目中引入 Log4j2 作为日志框架配置异步文件输出按日期滚动保留最近七天日志。日志里记录登录时间、下单单号、操作结果等摘要信息但绝不能打印完整密码或银行卡号这类敏感数据。排查问题时打开当日日志文件重点搜 ERROR、WARN 前缀上线线索引后再去 Debug 代码。6. 避坑指南从环境到业务的 8 个高频踩坑记录6.1 数据库连不上驱动版本、时区与认证方式的三方夹击现象容器启动时报 Cannot create PoolableConnectionFactory或者后台登录时页面提示获取连接失败。原因是 MySQL 8.0 默认使用 caching_sha2_password 认证插件而 5.x 驱动不认识这种认证方式另一个高频原因是连接 URL 没有配置时区MySQL 8 要求显式指定 serverTimezone否则报 CST 时区错误。解决方法对应行动驱动升级到 8.0.33URL 中追加 serverTimezoneAsia/Shanghai同时确认 MySQL 用户的 host 允许当前机器连接。如果是本地安装的 MySQL 8.0且账号密码正确但仍连不上还要检查是否使用了 root 而且 root 的加密插件是 caching_sha2_password。可以执行 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 密码 来切回旧版认证或者干脆建一个新用户并授权所有权限给这个项目使用。建议建专门用户修改 c3p0-config.xml 里对应的 user 配置即可别直接改 root 插件免得影响本地其他项目。6.2 页面中文全是问号或乱码现象前台商品名称显示为??后台添加的中文商品名变成乱码注册的中文用户名也变成乱码。原因分三层数据库连接 URL 缺 characterEncodingutf8、JSP 页面没有声明 UTF-8、数据库表字符集不是 utf8mb4。解决路径是先查数据库表字段字符集执行 SHOW CREATE TABLE goods看到 DEFAULT CHARSET 是 latin1 就说明建表脚本有问题需要改表字符集然后在连接 URL 中补上字符集参数最后确认编码过滤器已生效。还有一个容易忽略的场景修改完 JSP 后页面还是乱码但 Tomcat 控制台日志里正常。这种一般是浏览器缓存里存了旧的页面渲染结果强制刷新或者打开无痕窗口就能看到实际效果不属于代码问题。建议排查乱码的顺序遵循“访问 URL → 网络响应头 → JSP 页面声明 → 数据库数据”这条链路不要先改 JSP浪费时间。6.3 后台页面直接访问 404 或权限校验失效现象管理员登录成功进入后台可以打开商品管理页但直接在浏览器地址栏输入后台 JSP 地址却提示 404或者反过来未登录状态下直接输入后台地址竟然可以访问到页面。前者一般是 JSP 放在 WEB-INF 下导致的WEB-INF 下的资源只能通过 Servlet 转发访问这是正常现象不是 bug后者则说明过滤器没有拦住请求路径需要检查 web.xml 中过滤器的 url-pattern 是否覆盖了后台路径。我常见的做法是把后台 JSP 统一放到 /WEB-INF/admin 下且所有后台操作走 AdminServlet 统一分发不在 JSP 内直接访问。过滤器则拦截 /admin 前缀未登录跳转登录页登录但非管理员角色跳转首页。为了避免过滤器遗漏后台管理页面的所有链接统一从 AdminIndexServlet 发出不要在 JSP 里写死请求路径。url-pattern 配置为 /admin 和 /admin/* 两种分别对应精确拦截和路径通配拦截。6.4 商品图片不显示或上传失败现象商品列表里图片位置空白右键查看图片地址发现路径不存在。原因通常是项目上传目录与部署目录不一致开发时本地能显示换机器跑就丢了。解决方案是不要把图片上传到 IDE 的 target 目录或 Tomcat 的 webapps 目录建议使用一个独立的绝对路径存储如 D:/cake_shop_upload这样重启容器图片不丢换机器只需修改配置文件中上传根路径。数据库图片字段如果存的是相对路径页面取出来拼在项目根路径后面即可。要验证图片能访问要同时在 Tomcat 的 server.xml 里配置一个虚拟目录映射到实际图片路径把 URL 路径与本地文件系统路径关联。如果不配置虚拟目录即使本地有图片文件浏览器也访问不到。6.5 日期与时间相差 8 小时现象订单创建时间比当前时间晚 8 小时或者早 8 小时。原因是 JDBC URL 没有配置 serverTimezone或者配置了 UTC中国标准时间是东八区使用 UTC 会导致时间差。解决的方法是把 serverTimezone 改为 Asia/Shanghai同时在数据库连接初始化后执行 SET time_zone 8:00两者配合可以彻底纠正时区。如果你的数据库存 timestamp 类型建议统一使用 Java 端时间戳创建时间传 NOW() 给数据库也可以但两种方式不要混用否则排查起来很麻烦。6.6 部署后首次访问极慢之后正常现象项目刚启动后第一次请求响应时间十几秒刷新后就立刻恢复。原因是 Tomcat 在首次请求时才会编译 JSP 文件编译过程耗时较长属于正常现象。对课设演示来说建议正式演示前先热启动一遍项目把所有主要页面都点一遍触发 JSP 预编译演示时就会很流畅。如果想要彻底解决可以在 Tomcat 的 web.xml 中启用 JSP 预编译特性或者直接启动时访问首页触发编译。6.7 导出项目后换了机器依然跑不起来现象项目拷贝到另一台电脑后报找不到类或数据库空指针。原因通常是漏拷了数据库脚本或者忘记在新机器导入数据库也有可能是连接配置里的绝对路径失效比如上传目录和日志目录还在引用原机器的路径。解决路径是新机器上按顺序完成建库、建表、导入初始数据、修改连接配置、确认驱动 jar 已在 WEB-INF/lib 下这五步如果用的是 Maven 构建还需要确认本地仓库已下载对应依赖不要假设 IDE 会把依赖一起导出。7. 进阶拓展把普通课设提升为答辩高分项目的四个方向7.1 用 JSTL 重构所有 JSP 页面彻底消灭 Java 脚本片段我评审过不少同题项目最常见的一个代码异味就是 JSP 里散落着大量 % % 和不规范的小脚本这些不仅难维护而且容易让答辩印象分打折扣。如果时间允许可以把每个 JSP 页面里的 Java 脚本片段全部替换为 JSTL 核心标签和 EL 表达式。循环用 c:forEach条件判断用 c:if 或 c:chooseURL 拼接用 c:url 自动处理上下文路径。具体来说商品列表页原本可能是一段 for 循环拼 HTML 的 Java 代码重构成 JSTL 后大概是先用 request.setAttribute(goodsList, list) 在 Servlet 层准备数据JSP 页面里用 c:forEach items${goodsList} 遍历并渲染卡片。这样做的好处有三个页面可读性大幅提升静态代码审查时不会一眼看到脚本拼接页面代码与业务逻辑彻底分离后续改成 Vue 模板也容易理解答辩时可以主动提“视图层使用 JSTL 渲染避免在页面内嵌业务逻辑”这比被动回答代码问题更能体现工程意识。7.2 增加拦截器记录用户操作日志并做简单的数据统计做一个后台操作日志模块用一个日志表记录每次后台操作的时间、操作人、操作类型。实现方法是新增一个 LogFilter在 doFilter 中记录请求的方法、URI、执行时间和状态码异步写入日志表。重点只关注写操作POST、PUT、DELETE 才有记录价值GET 请求容易产生大量无用日志。这个功能在答辩演示中效果非常直观老师随便修改一条商品信息刷新后台日志页就能看到完整记录。还可以在后台首页增加简易的销售统计比如按商品统计售出数量排行、按日期统计订单量。SQL 只需要对订单明细表做 GROUP BY 商品 ID再关联商品表取名称排序显示前十条即可。这部分的图表可以用前端图表库 ECharts 的柱状图或折线图展示数据由 Servlet 提供 JSON 格式接口页面用 fetch 获取后渲染会让整个项目有真正的后台管理系统质感。7.3 为关键接口补充防御性参数校验防御性编程高度提分。当前项目中的商品数量、订单收货人信息、用户联系方式可能都没有充分校验。比如数量应限制在合法整数范围超过一定范围直接拒绝操作。更重要的是有些系统会把价格、金额这类敏感参数放在隐藏字段传回后台这是一种高风险设计正确的做法是价格一律以数据库为准页面上的价格仅作展示后台从商品表按 ID 重新读取并计算订单金额。我习惯在 Servlet 入口统一写一个参数校验工具类部分接一个字符串参数非空判断、长度判断、格式匹配不通过就返回结果对象的错误码只有校验全部通过才进入 Service 层。这类代码不复杂但在演示时可以主动演示“如果绕过页面直接构造出非法参数”系统会拦截而不是报 500 错误。7.4 丰富前端交互细节成本最低的体验提升前端视觉会直接影响演示的整体感染力但不需要重写前端框架。可以做的低成本优化有商品列表页用图片懒加载图片进入可视区域时再异步加载大促式页面效果提升明显列表页加一个简单的下拉筛选与排序用 JavaScript 实现而不需要重新加载页面购物车页边输入数量边自动重新计算小计与总价在输入框的 change 事件里调用计算函数。这些细节无需修改后端接口纯前端即可完成但对“观赏体验”提升非常大。个人习惯上我会在项目正式演示之前做一份部署清单把数据库导入、配置修改、启动顺序、演示账号、演示话术的关键点列出来一条一条打勾。这份清单平时看着啰嗦但换机器演示时能救回非常尴尬的时间。以上四个方向任选其一投入时间都能让这套 JSP Servlet 甜品电商项目在答辩中达到“完整作品”的级别而不是停留在“能跑”的水平。做这个项目最大的收获不是背熟了几段代码而是理解了 Web 应用从请求到响应、从页面到数据库的完整闭环。你之后学任何新框架其实都是在这个闭环上加更高效的封装。希望这篇笔记中的部署思路和避坑经验能帮到你少走一段弯路也祝你顺利完成这个高分项目。本文还有配套的精品资源点击获取