JavaWeb原生商城实战:Servlet+JSP+JDBC全链路电商系统
2026/9/12 17:28:14 网站建设 项目流程

简介:本资源是一套高质量JavaWeb课程设计级仿小米在线商城系统源码,面向高校计算机专业学生及JavaWeb初学者,用于完成大作业、课程实训或Web开发能力进阶实践。项目已通过严格调试,评审得分95分以上,功能完整覆盖用户注册登录、商品浏览、购物车管理、订单提交与后台管理等核心电商模块,技术栈涵盖Servlet、JSP、JDBC及基础前端三件套(HTML/CSS/JS),并集成Bootstrap响应式框架与TinyMCE富文本编辑器。压缩包共467个文件,含39个Java业务逻辑类、28个JSP页面、25个JS交互脚本、19个CSS样式文件、19个Jar依赖库及2个SQL数据库脚本,辅以大量图片资源(PNG/JPG/GIF)和少量视频、配置文件,整体大小23.98MB。目前已有1552人学习下载,配套结构清晰、注释规范,包含可直接导入运行的数据库与完整目录层级,特别适合理解MVC分层架构与前后端协同开发流程。

1. 这不是套模板的商城Demo,而是一个能跑通「用户注册→商品浏览→购物车→订单提交→后台管理」全链路的JavaWeb实战项目

很多同学拿到“JavaWeb仿小米商城”源码,第一反应是:又一个带Bootstrap页面+Servlet跳转+JDBC查库的静态演示?但这个95分大作业项目完全不同——它在Tomcat 8.5+MySQL 5.7环境下完整实现了会话级购物车持久化、基于角色的RBAC后台权限控制、商品SKU规格联动(如颜色/内存组合库存校验)、以及真实可用的订单状态机(待支付→已发货→已完成→已取消)。项目没有用Spring Boot自动装配偷懒,所有DAO层SQL手写带参数预编译,Service层事务边界清晰,Filter完成登录态拦截与CSRF防护。适合两类人:一是刚学完JSP/Servlet/EL/JSTL想验证知识闭环的大三学生;二是需要快速搭建教学案例、但拒绝“HelloWorld式假数据”的实训课教师。它不追求炫技,但每个模块都经得起debug单步跟踪。


2. 技术栈选型与工程结构解析:为什么用原生JavaWeb而非Spring Boot?

2.1 为什么坚持使用Servlet 3.1 + JSP + JDBC原生组合?

当前主流教学场景中,Spring Boot因自动配置简化了开发,却模糊了HTTP协议本质、容器生命周期、线程安全边界等核心概念。本项目刻意回归Servlet规范,原因有三:
第一,明确请求处理链路:从web.xml<servlet-mapping>定义URL路径,到HttpServlet#doGet/doPost方法接收原始HttpServletRequest对象,再到RequestDispatcher#forward完成MVC跳转,每一步都可打断点观察request.getAttribute()request.getParameter()的差异;
第二,暴露数据库连接管理痛点:项目采用DBUtil工具类封装Connection获取与关闭逻辑,而非依赖Spring的DataSourceTransactionManager。这迫使开发者直面Connection未关闭导致的连接池耗尽问题——在OrderServlet.java中你能看到典型的try-with-resources写法:

try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement("INSERT INTO orders (...) VALUES (?)")) { ps.setString(1, userId); ps.executeUpdate(); } catch (SQLException e) { // 显式回滚逻辑必须手动编写 conn.rollback(); throw new RuntimeException(e); }

提示:DBUtil.getConnection()内部使用DriverManager.getConnection()而非连接池,这是教学项目有意为之——让学生先理解“连接创建开销”,再过渡到c3p0Druid配置。

第三,强化JSP EL表达式与自定义标签实践:项目中cart.jsp使用${cartItems[0].productName}直接访问List元素,product_list.jsp通过<c:forEach>遍历商品列表,而admin/user_list.jsp则嵌入自定义标签<my:pageNav>实现分页导航。这种写法虽不如Thymeleaf类型安全,但能强制理解JSP翻译为Servlet后的字节码执行过程。

2.2 工程目录结构与关键文件职责映射

项目采用标准JavaWeb目录布局,但存在几个易被忽略的设计细节:

目录路径关键文件核心职责常见误用点
/WEB-INF/web.xmlweb.xml定义Servlet映射、Filter链、Session超时时间(设为30分钟)修改<session-config>后未重启Tomcat导致失效
/src/com/mi/dao/UserDao.java,ProductDao.java封装CRUD操作,SQL语句全部硬编码(无XML映射)insertUser()方法中password字段未调用BCrypt.hashpw()加密
/src/com/mi/service/OrderService.java实现订单创建事务:先扣减库存再生成订单,任一失败则全局回滚忘记在catch块中调用conn.rollback()
/WebContent/static/css/style.css,bootstrap.min.css页面样式,其中style.css覆盖Bootstrap默认按钮圆角与字体大小register.css.form-control宽度设为100%,导致响应式失效
/WebContent/WEB-INF/jsp/admin/index.jsp,user/order_detail.jsp视图层,使用<%@ include file="header.jsp" %>复用头部index.jsp<c:if test="${empty sessionScope.user}">未加<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>声明
2.2.1 数据库表设计体现业务约束逻辑

MySQL建表脚本(mi_store.sql)中,products表包含stock字段并设为INT NOT NULL DEFAULT 0,而order_items表通过FOREIGN KEY (product_id) REFERENCES products(id)建立强关联。更关键的是orders表的status字段采用ENUM('pending','paid','shipped','completed','cancelled')而非VARCHAR,这在DAO层OrderDao.updateStatus()方法中直接映射为Java枚举OrderStatus.PAID,避免字符串硬编码错误。执行建库命令时需注意字符集:

mysql -u root -p --default-character-set=utf8mb4 < mi_store.sql

注意:utf8mb4是必须项,否则商品名称中的emoji(如📦)会存储为?,且web.xml<filter>配置的CharacterEncodingFilter必须设置encoding="UTF-8",否则中文搜索关键词乱码。


3. 核心功能模块实现:从登录认证到订单状态流转

3.1 基于Session的用户认证与权限控制

项目未使用Shiro或Spring Security,而是通过LoginServletAuthFilter构建轻量级认证体系。流程如下:

  1. 用户提交login.jsp表单,LoginServlet接收usernamepassword参数;
  2. UserDao.checkLogin()执行SQL:SELECT id, username, password, role FROM users WHERE username=? AND password=MD5(?)
  3. 认证成功后将User对象存入HttpSessionsession.setAttribute("user", user)
  4. AuthFilter拦截所有/admin/*路径请求,检查session.getAttribute("user")是否存在且role.equals("admin")

关键代码在AuthFilter.java中:

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; // 放行登录页与静态资源 if (request.getRequestURI().endsWith("/login.jsp") || request.getRequestURI().contains("/static/")) { chain.doFilter(req, resp); return; } HttpSession session = request.getSession(false); User user = session != null ? (User) session.getAttribute("user") : null; if (user == null || !"admin".equals(user.getRole())) { response.sendRedirect(request.getContextPath() + "/login.jsp?error=access_denied"); return; } chain.doFilter(req, resp); }

提示:request.getSession(false)避免无意义创建Session,response.sendRedirect()使用相对路径确保跨上下文部署兼容性。

3.2 购物车功能的两种实现模式对比

项目提供两种购物车方案,分别对应不同教学目标:

  • Session购物车(默认启用):CartServletCartItem对象存入session.getAttribute("cart"),优点是无需数据库交互,缺点是用户关闭浏览器后购物车清空;
  • 数据库购物车(需手动开启):修改CartServlet.javaaddCartItem()方法,将CartItem插入cart_items表,并关联user_id。此时需在web.xml中配置<session-config><tracking-mode>COOKIE</tracking-mode></session-config>确保Cookie传递。

CartItem类的关键字段设计体现电商特性:

public class CartItem { private int id; // 主键,用于数据库购物车 private String productId; // 商品ID private String productName; // 商品名称(冗余,避免联表查询) private double price; // 单价(快照价格,防止商品调价影响订单) private int quantity; // 数量 private String spec; // 规格描述,如"黑色/128GB" }
3.2.1 SKU库存校验的硬编码实现

当用户添加“小米14 Pro 256GB”到购物车时,系统需校验该SKU库存是否充足。ProductDao.getStockBySpec()方法执行以下SQL:

SELECT stock FROM product_skus WHERE product_id = ? AND spec = ?

若返回值小于quantity,则CartServlet返回JSON:{"success":false,"message":"库存不足"}。此逻辑未使用乐观锁(如UPDATE products SET stock=stock-1 WHERE id=? AND stock>=?),教学场景下更强调业务规则显式表达。

3.3 订单创建的事务边界与异常处理

OrderServlet处理订单提交的核心逻辑位于createOrder()方法,其事务控制严格遵循JDBC规范:

public void createOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交 // 步骤1:扣减库存(锁定行) String reduceSql = "UPDATE product_skus SET stock = stock - ? WHERE product_id = ? AND spec = ?"; try (PreparedStatement ps1 = conn.prepareStatement(reduceSql)) { ps1.setInt(1, quantity); ps1.setString(2, productId); ps1.setString(3, spec); int affected = ps1.executeUpdate(); if (affected == 0) throw new RuntimeException("库存不足"); } // 步骤2:插入订单主表 String insertOrderSql = "INSERT INTO orders (...) VALUES (...)"; // ... 执行插入 // 步骤3:插入订单明细 String insertItemSql = "INSERT INTO order_items (...) VALUES (...)"; // ... 执行插入 conn.commit(); // 全部成功才提交 response.getWriter().write("{\"success\":true}"); } catch (Exception e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) {} } response.getWriter().write("{\"success\":false,\"message\":\"" + e.getMessage() + "\"}"); } finally { DBUtil.closeConnection(conn); } }

注意:conn.setAutoCommit(false)必须在获取Connection后立即调用,且rollback()需在catch块中显式执行,否则事务回滚失效。


4. 数据库初始化与运行环境配置实操指南

4.1 MySQL 5.7环境适配要点

项目数据库脚本mi_store.sql针对MySQL 5.7设计,需特别注意以下三项配置:

  1. 禁用严格模式:MySQL 5.7默认开启STRICT_TRANS_TABLES,会导致INSERT INTO users (username) VALUES ('test')password字段为NOT NULL而失败。修改my.cnf
[mysqld] sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES # 改为 sql_mode=NO_ENGINE_SUBSTITUTION
  1. 时区校准orders.created_time字段类型为DATETIME,若服务器时区为CST(中国标准时间),需在JDBC URL中显式指定:
jdbc.url=jdbc:mysql://localhost:3306/mi_store?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
  1. 外键约束启用product_skus表依赖products.id,建表前需确认innodb_file_per_table=ONforeign_key_checks=1
4.1.1 初始化数据库的完整命令流
# 1. 创建数据库(指定字符集) mysql -u root -p -e "CREATE DATABASE mi_store CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 2. 导入SQL(注意路径) mysql -u root -p mi_store < /path/to/mi_store.sql # 3. 验证表结构 mysql -u root -p -D mi_store -e "SHOW TABLES;" mysql -u root -p -D mi_store -e "DESCRIBE products;"

执行后应看到12张表,包括usersproductsordersorder_itemsproduct_skus等核心业务表。

4.2 Tomcat 8.5部署与调试配置

项目要求Tomcat 8.5(兼容Servlet 3.1),部署步骤如下:

  1. 将解压后的JavaWeb仿小米在线商城源码文件夹重命名为mi-store
  2. 复制到$CATALINA_HOME/webapps/目录下;
  3. 启动Tomcat:$CATALINA_HOME/bin/startup.sh(Linux)或startup.bat(Windows);
  4. 访问http://localhost:8080/mi-store/,首页应显示小米风格轮播图。

若出现HTTP Status 404,检查以下三点:

  • webapps/mi-store/WEB-INF/web.xml是否存在且格式正确;
  • webapps/mi-store/WEB-INF/classes/下是否有编译后的.class文件(需先用IDE导出WAR或手动javac编译);
  • catalina.out日志中是否报ClassNotFoundException: com.mysql.jdbc.Driver——此时需将mysql-connector-java-5.1.47.jar放入$CATALINA_HOME/lib/目录。

提示:Eclipse中部署时,右键项目→Properties→Targeted Runtimes需勾选Apache Tomcat v8.5,且Deployment Assembly/src需映射到/WEB-INF/classes


5. 前端样式调试与响应式适配技巧

5.1 Bootstrap 3.x与自定义CSS的冲突解决

项目使用Bootstrap 3.3.7(bootstrap.min.css),但style.css中大量覆盖规则导致移动端失效。典型问题:navbar在小屏下不折叠,col-md-4栅格错位。根本原因是style.css.navbar设置了display: block !important,强行覆盖Bootstrap的@media (max-width: 767px)断点。

修复步骤:

  1. 打开WebContent/static/css/style.css
  2. 搜索.navbar,注释掉display: block !important
  3. @media (max-width: 767px)区块内添加:
@media (max-width: 767px) { .navbar-header { float: none; } .navbar-toggle { display: block; } .navbar-collapse { border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); } .navbar-nav { float: none; margin: 7.5px -15px; } .navbar-nav>li { float: none; } .navbar-nav>li>a { padding-top: 10px; padding-bottom: 10px; } }
5.1.1 图片懒加载与轮播图性能优化

首页轮播图<img src="images/banner1.jpg">未做懒加载,导致首屏渲染阻塞。可在index.jsp中替换为:

<img>.lazy { opacity: 0; transition: opacity 0.3s ease-in; } .lazy[src] { opacity: 1; }

5.2 表单验证的客户端增强

register.jsp仅依赖HTML5required属性,缺乏邮箱格式校验。在<head>中加入:

<script> function validateEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return re.test(email); } document.getElementById("registerForm").onsubmit = function(e) { const email = document.getElementById("email").value; if (!validateEmail(email)) { alert("请输入有效的邮箱地址"); e.preventDefault(); return false; } }; </script>

注意:客户端验证仅为体验优化,RegisterServlet中仍需用String.matches()二次校验,防止绕过。


6. 生产环境部署前的三项关键检查

6.1 SQL注入与XSS漏洞加固点

项目虽经调试,但仍有两处高危风险需手动修复:

  • 搜索功能SQL拼接SearchServlet.javaString sql = "SELECT * FROM products WHERE name LIKE '%" + keyword + "%'",必须改为预编译:
String sql = "SELECT * FROM products WHERE name LIKE ?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); // ... 执行查询 }
  • 商品详情页XSS反射product_detail.jsp${param.id}直接输出,应使用JSTL<c:out value="${param.id}" />或Java代码转义:
<% String id = request.getParameter("id"); if (id != null) { id = id.replaceAll("<", "&lt;").replaceAll(">", "&gt;"); } %> <div>商品ID:<%= id %></div>

6.2 日志输出规范化配置

当前项目使用System.out.println()打印日志,生产环境需替换为Log4j。在src/log4j.properties中添加:

log4j.rootLogger=INFO, stdout, file log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %c{1}:%L - %m%n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=${catalina.home}/logs/mi-store.log log4j.appender.file.MaxFileSize=10MB log4j.appender.file.MaxBackupIndex=5 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %c{1}:%L - %m%n

然后在OrderService.java中替换System.out.println("订单创建成功")为:

private static final Logger logger = Logger.getLogger(OrderService.class); logger.info("订单ID " + orderId + " 创建成功");

6.3 数据库连接池替换实操

原项目使用DriverManager.getConnection(),高并发下易耗尽连接。推荐替换为Druid连接池,步骤如下:

  1. 下载druid-1.2.16.jar放入WEB-INF/lib/
  2. 创建src/druid.properties
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mi_store?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username=root password=123456 initialSize=5 minIdle=5 maxActive=20 maxWait=60000 timeBetweenEvictionRunsMillis=60000 minEvictableIdleTimeMillis=300000 validationQuery=SELECT 1 testWhileIdle=true testOnBorrow=false testOnReturn=false poolPreparedStatements=true maxPoolPreparedStatementPerConnectionSize=20 filters=stat,wall,log4j
  1. 修改DBUtil.javagetConnection()方法:
private static DruidDataSource dataSource; static { Properties props = new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream("druid.properties")); dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(props); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }

提示:Druid监控页面可通过http://localhost:8080/mi-store/druid/index.html访问,需在web.xml中配置DruidStatViewServletDruidWebStatFilter

最后验证订单流程:注册用户→浏览商品→加入购物车→提交订单→登录后台查看订单列表。每一步都应有对应数据库记录生成,且orders.status字段按业务规则准确变更。

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

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

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

立即咨询