JSP购物系统源码实战:从环境搭建到分层避坑

发布时间:2026/10/7 5:34:52
JSP购物系统源码实战:从环境搭建到分层避坑 简介这份资源是一套基于JSP、MySQL与Tomcat的网上购物系统Java源码面向正在学习Java Web开发、希望以完整项目理解电商业务逻辑的初学者与进阶开发者。系统涵盖用户注册登录、商品分类展示与搜索、购物车增删改与总价计算、订单下单支付与状态管理、商品评论等核心模块后端以Servlet与JavaBean处理业务JSP负责页面渲染MySQL存储商品、用户与订单数据Tomcat承载应用运行是理解前后端交互与数据库表设计的实战案例。压缩包共787个文件约11.02MB包含75个java源文件、63个jsp页面、75个class编译文件、38个jar依赖包以及gif、jpg等页面素材和xml、sql、properties等配置与建库脚本目录结构完整便于按模块阅读与二次开发。目前已有115人学习下载适合作为课程设计、毕业设计或自学练手项目帮助读者掌握Java Web开发流程与常见问题处理思路。1. 从一份 class 清单说起这套 JSP 购物系统到底能跑出什么很多人第一次拿到「网上购物系统jspmysqltomcat」这类 Java 源码包解压后看到的是一堆.class和.java混在一起的文件比如CartService.class、OrderDaoImpl.class、ProductDaoImpl.class、UserDaoImpl.class、CommentDaoImpl.class、CategoryDaoImpl.class还有Order.class、Comment.class这些实体类。第一反应往往是「这能直接跑吗」。答案是能但前提是你得先把编译产物和源码的关系理清楚再把数据库和 Tomcat 这两条腿接上。这套系统的定位很明确——它是一个用 JSP Servlet JavaBean 三层结构搭起来的电商练手项目后端业务逻辑用 Java 写页面展示交给 JSP数据落在 MySQL运行容器是 Tomcat。它覆盖了用户注册登录、商品分类展示、购物车增删改、订单生成与状态流转、商品评论这几条主线。适合两类人一是刚学完 Java Web 基础、想找一个完整项目把 Servlet 生命周期、DAO 模式、JSP 内置对象串起来的学生二是工作里要维护老 JSP 项目、需要快速摸清「Service-Dao-Entity」分层套路的开发者。下面我按「先跑起来、再拆结构、最后避坑」的顺序把这份源码的落地路径拆开讲。2. 环境搭建与数据库初始化把 Tomcat 和 MySQL 接上2.1 JDK、Tomcat、MySQL 的版本对齐这套源码用的是 JSP Servlet 技术栈没有 Spring Boot 那种内嵌容器所以必须外挂 Tomcat。常见做法是 JDK 8 Tomcat 8.5 或 9 MySQL 5.7/8.0 这个组合兼容性最稳。如果你用 JDK 17 以上配 Tomcat 10会遇到javax.servlet包名被改成jakarta.servlet的问题源码里所有import javax.servlet.*全部报错这是新手最容易翻车的地方。安装顺序建议先装 JDK配好JAVA_HOME再解压 Tomcat配CATALINA_HOME最后装 MySQL记住 root 密码。验证 JDK 和 Tomcat 是否就绪用下面两条命令# 检查 JDK 版本确认输出 1.8.x 或 8.x java -version # 启动 TomcatLinux/macOS 用 startup.shWindows 用 startup.bat # 启动后访问 http://localhost:8080 看到 Tomcat 默认页即成功 $CATALINA_HOME/bin/startup.shjava -version输出里如果带OpenJDK或HotSpot都正常关键是版本号要对上。Tomcat 启动脚本里的startup.sh会去读JAVA_HOME如果这个变量没配脚本会直接报Neither the JAVA_HOME nor the JRE_HOME environment variable is defined这时候别去改脚本先把环境变量补上。2.2 建库建表与连接配置源码包里一般会带一个.sql文件常见命名是shop.sql或db.sql。导入之前先建库字符集用utf8mb4否则商品名里的中文和 emoji 会变问号-- 创建数据库字符集必须指定 utf8mb4 CREATE DATABASE shop_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 切换到该库 USE shop_db; -- 导入表结构在命令行里执行不要在客户端工具里手动粘贴大文件 -- mysql -u root -p shop_db shop.sql导入完成后用SHOW TABLES;确认表数量通常会有user、product、category、cart、orders、order_item、comment这几张。接着找到源码里的数据库连接配置老项目一般写在src/db.properties或者某个DBUtil.java里形如# db.properties 典型配置 jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/shop_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password你的密码这里有两个参数必须注意serverTimezoneAsia/Shanghai不加会在 MySQL 8.0 下报时区错误useSSLfalse不加会有 SSL 连接警告虽然不影响运行但日志很吵。驱动类名com.mysql.cj.jdbc.Driver是 MySQL 8.0 的写法如果你用的是 5.7 的 jar 包要改成com.mysql.jdbc.Driver否则报ClassNotFoundException。2.3 编译与部署到 Tomcat源码里那些.class文件是编译产物如果你改了.java源码必须重新编译。用javac手动编译时要把 servlet-api.jar 加进 classpath# 编译所有 java 文件-d 指定输出目录-cp 引入 Tomcat 的 servlet-api javac -encoding UTF-8 -d WebContent/WEB-INF/classes \ -cp $CATALINA_HOME/lib/servlet-api.jar \ src/com/shop/**/*.java-encoding UTF-8不加的话源码里的中文注释和字符串会乱码编译出来的 class 里中文全是问号。-d指向WEB-INF/classes是因为 Tomcat 的类加载器只认这个目录下的 class。编译完把整个 WebContent 目录或打包成 war丢进$CATALINA_HOME/webapps/重启 Tomcat访问http://localhost:8080/你的项目名/就能看到首页。如果 404先看 Tomcat 日志logs/catalina.out八成是web.xml里 servlet 映射写错或者 class 没编译进去。3. 分层结构拆解Service、Dao、Entity 各管什么3.1 从 CartService 和 OrderService 看业务层职责CartService.class和OrderService.class是业务层的核心。Service 层的作用是编排逻辑比如「加入购物车」这个动作Service 里要做的不是直接写 SQL而是先调ProductDaoImpl查商品是否存在、库存够不够再调CartDaoImpl写入购物车记录最后返回结果给 Servlet。这种分层的好处是 Servlet 只负责收参数、调 Service、转发 JSP逻辑清晰。以购物车为例Service 里常见的方法签名是addToCart(int userId, int productId, int quantity)。参数里userId从 session 里取productId从前端表单取quantity默认 1。Service 内部会先判断该用户购物车里是否已有该商品有就累加数量没有就新增一条记录。这个「先查后写」的逻辑如果放在 Servlet 里代码会臃肿到没法维护所以分层不是形式主义是实打实降低耦合。3.2 DaoImpl 里的 JDBC 套路与 SQL 注入防线UserDaoImpl、ProductDaoImpl、OrderDaoImpl、CommentDaoImpl、CategoryDaoImpl这五个 Dao 实现类基本覆盖了所有表的增删改查。老项目里 Dao 层通常用原生 JDBC代码结构是「获取连接 → 创建 PreparedStatement → 设参数 → 执行 → 处理 ResultSet → 关闭资源」。// ProductDaoImpl 里按分类查商品的典型写法 public ListProduct findByCategory(int categoryId) { ListProduct list new ArrayList(); String sql SELECT * FROM product WHERE category_id ? ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, categoryId); // 参数下标从 1 开始不是 0 try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Product p new Product(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setPrice(rs.getBigDecimal(price)); list.add(p); } } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码里PreparedStatement的?占位符是防 SQL 注入的关键千万别图省事用字符串拼接。ps.setInt(1, categoryId)的下标从 1 开始这是 JDBC 规范写 0 会报Parameter index out of range。try-with-resources语法能自动关闭连接比手动在finally里close()更安全老代码如果没这么写你要留意连接泄漏问题——Tomcat 跑久了报Too many connections多半就是这里漏了。3.3 Entity 实体类与 JSP 页面的数据传递Order.class、Comment.class这些实体类就是 POJO字段和数据库表一一对应提供 getter/setter。它们的作用是承载数据在层与层之间传递。Servlet 查到数据后用request.setAttribute(productList, list)塞进去JSP 页面用 JSTL 的c:forEach遍历展示。!-- product_list.jsp 里遍历商品列表 -- c:forEach items${productList} varp div classproduct-item h3${p.name}/h3 span${p.price}/span a hrefcart?actionaddid${p.id}加入购物车/a /div /c:forEach${productList}对应 Servlet 里setAttribute的 key名字必须完全一致大小写敏感。varp是循环变量${p.name}会去调Product类的getName()方法。如果页面显示空白先检查实体类有没有写 getterJSTL 表达式依赖 getter 而不是字段本身。另外 JSP 页面顶部要引入 JSTL 标签库% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %漏了这行会报The absolute uri cannot be resolved。4. 核心功能链路从登录到下单的完整走查4.1 用户登录与 Session 管理登录流程是JSP 表单提交用户名密码 →UserServlet接收 → 调UserDaoImpl.findByUsername()查用户 → 比对密码 → 成功则session.setAttribute(user, user)→ 重定向到首页。这里有个常见坑密码在数据库里是明文还是 MD5。老项目很多是明文存储你如果要改成 MD5得同时改注册和登录两处只改一处会导致新用户能注册但登不上。Session 管理要注意超时设置Tomcat 默认 30 分钟。在web.xml里可以改session-config session-timeout60/session-timeout !-- 单位是分钟 -- /session-config登录拦截一般用 Filter 实现判断session.getAttribute(user)是否为空为空就跳登录页。Filter 的url-pattern要排除登录页和静态资源否则会死循环重定向。4.2 购物车与订单的状态流转购物车到订单的转换是这套系统的核心链路。用户在购物车页点「结算」OrderServlet会做几件事从 session 取 userId → 从购物车表查出所有条目 → 生成订单主记录orders表→ 生成订单明细order_item表→ 清空购物车 → 跳转订单详情页。这几步要么全成功要么全回滚所以 Service 层应该用事务包起来。// OrderService 里下单的事务控制 public boolean createOrder(int userId) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交开启事务 // 1. 插入订单主表 int orderId orderDao.insertOrder(conn, userId); // 2. 插入订单明细 orderItemDao.insertItems(conn, orderId, cartDao.findByUser(conn, userId)); // 3. 清空购物车 cartDao.clearByUser(conn, userId); conn.commit(); // 三步都成功才提交 return true; } catch (SQLException e) { if (conn ! null) try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { if (conn ! null) try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }关键点是conn.setAutoCommit(false)之后所有 Dao 方法必须共用同一个 Connection 对象不能各自去DBUtil.getConnection()否则事务管不到它们。这是新手写事务最容易犯的错——以为加了setAutoCommit(false)就万事大吉结果每个 Dao 内部又开了新连接回滚根本不起作用。4.3 商品评论与分类展示评论模块相对简单CommentDaoImpl提供insert和findByProductId两个方法。用户提交评论后Servlet 调 insert然后重定向回商品详情页详情页再查一次评论列表展示。这里要注意 XSS 防护评论内容直接输出到 JSP 时要用c:out value${comment.content} /而不是${comment.content}后者会把 HTML 标签原样渲染有人提交scriptalert(1)/script就会弹窗。分类展示依赖CategoryDaoImpl查所有分类然后在页面顶部渲染导航栏。分类和商品是一对多关系product表里有category_id外键。如果删除分类时没有先处理关联商品会报外键约束错误常见做法是删除前先把该分类下商品移到默认分类或者用逻辑删除加is_deleted字段代替物理删除。5. 避坑与排查那些让项目跑不起来的常见问题5.1 中文乱码从请求到响应的三层排查现象页面输入的中文提交后变成乱码或者数据库里查出来的中文显示问号。原因通常有三层第一层是 JSP 页面编码第二层是 Servlet 请求编码第三层是数据库连接编码。解决顺序是从上往下查。JSP 页面顶部加% page contentTypetext/html;charsetUTF-8 %Servlet 里在doPost第一行加request.setCharacterEncoding(UTF-8)数据库连接 URL 里带characterEncodingutf8。三层都对了才不会乱码只改一层往往没用。5.2 ClassNotFoundException驱动包没放进 WEB-INF/lib现象Tomcat 启动后访问页面报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因MySQL 的 jar 包没有放到WEB-INF/lib目录下或者放了但 Tomcat 没重新加载。解决把mysql-connector-java-8.0.x.jar复制到WEB-INF/lib重启 Tomcat。注意不要只放在$CATALINA_HOME/lib虽然那样也能用但项目移植到别的 Tomcat 上就会漏。放在WEB-INF/lib是项目自包含的做法更稳妥。5.3 404servlet 映射与编译输出路径对不上现象首页能打开但点登录或加入购物车报 404。原因web.xml里url-pattern配的路径和表单action不一致或者.class文件没编译到WEB-INF/classes对应包路径下。解决先看web.xml里 servlet 的url-pattern再确认WEB-INF/classes/com/shop/servlet/下有没有对应的 class 文件。如果 class 文件在但还 404检查包名和目录层级是否一致package com.shop.servlet;对应的目录必须是com/shop/servlet/少一层都不行。5.4 数据库连接池耗尽Too many connections现象项目跑一段时间后所有页面都报数据库连接失败MySQL 日志显示Too many connections。原因Dao 层每次操作都new一个 Connection 但没关闭或者ResultSet、Statement没关导致连接泄漏。解决把所有 JDBC 操作改成try-with-resources写法确保 Connection、PreparedStatement、ResultSet 都在 try 的括号里声明。如果项目规模上来了建议引入 Druid 或 C3P0 连接池在db.properties里配initialSize、maxActive、maxWait这几个参数比裸 JDBC 稳定得多。5.5 JSP 页面修改后不生效现象改了 JSP 文件刷新浏览器还是旧内容。原因Tomcat 会缓存编译后的 JSP Servlet默认不检查更新。解决在web.xml里把 JSP 的development模式打开或者直接删掉$CATALINA_HOME/work/Catalina/localhost/项目名/下的缓存目录再重启。开发阶段建议在conf/web.xml里把development设为true、modificationTestInterval设为0这样每次请求都会检查 JSP 是否更新。6. 进阶技巧用 Filter 统一编码与登录校验跑通之后你会发现每个 Servlet 里都写一遍request.setCharacterEncoding(UTF-8)很烦登录校验也是每个页面都要判断 session。这时候用 Filter 统一处理是标准做法。下面这个 EncodingFilter 同时解决编码和登录拦截两件事// EncodingFilter.java统一编码 登录校验 WebFilter(/*) // 拦截所有请求 public class EncodingFilter implements Filter { // 不需要登录就能访问的路径白名单 private static final String[] WHITE_LIST {/login.jsp, /login, /register.jsp, /register, /css/, /js/}; Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 1. 统一设置请求和响应编码 request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); // 2. 判断是否在白名单内 String uri request.getRequestURI(); boolean isWhite false; for (String path : WHITE_LIST) { if (uri.contains(path)) { isWhite true; break; } } // 3. 非白名单且未登录跳转登录页 if (!isWhite request.getSession().getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; // 注意这里要 return否则请求会继续往下走 } chain.doFilter(req, resp); // 放行 } }WebFilter(/*)是 Servlet 3.0 的注解写法不用在web.xml里再配一遍。白名单用uri.contains(path)判断简单够用但如果项目大了建议用startsWith配合 contextPath 截取避免/css/匹配到/mycss/这种误伤。最关键的是sendRedirect之后必须return否则代码继续执行到chain.doFilter请求还是会被放行登录拦截就形同虚设——这个坑我当年踩过排查了半天才发现少写一个 return。另一个实用技巧是给 JSP 页面加一个公共的头部和尾部文件用% include fileheader.jsp %引入改导航栏时只改一个文件。但要注意静态 include 是编译时合并两个页面不能有同名变量否则会报重复定义。动态 include 用jsp:include pageheader.jsp /则是运行时包含各自独立编译更安全但性能略低。老项目里两种混用很常见你接手时先看用的是哪种再决定改哪里。从那以后我每次拿到这类 JSP 老项目都强制先跑一遍「JDK 版本 → 驱动包位置 → 数据库编码 → Filter 白名单」这四步检查确认无误再动业务代码。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询