十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JavaWeb购物商城项目实战:从IDEA创建到Tomcat部署全流程

JavaWeb购物商城项目实战:从IDEA创建到Tomcat部署全流程 简介面向初学JavaWeb学生与开发者的购物商城项目源码基于JSPServletMySQL实现定位为可运行的在线商店示例以酒店行业商品销售为背景重点展示商品管理、购物车、订单支付、库存更新等典型电商业务流程。整套资料共270个文件压缩包仅7.08MB涵盖jsp页面、java与class源码、xml配置、css/js前端资源以及SQL数据库脚本另含较多gif动态演示方便对照操作效果其中class与java文件便于查看编译前后代码适合边读边练。目前已有1285人学习下载适合作为高校JavaWeb课程设计、毕业设计或项目实战的参考框架。通过研读源码可掌握用户注册登录、商品分类展示、购物车管理、订单提交与跟踪、管理员后台等完整功能模块的编码实现并理解DAO、Service、Action等经典分层思想与Servlet控制流程也可学习JSP内置对象、请求转发与重定向、JDBC操作等关键知识点。配套数据库脚本和配置文件有助于快速搭建Tomcat与MySQL环境从而在本地实际运行体验购物流程是一份完整性较高的实战学习材料。 带过不少实习生之后我有个很深的感触javaweb购物商城这个经典题目几乎人人都在做但大多数人的项目停留在“本地能跑”的程度。IDE 一点运行浏览器里能刷出商品列表课设答辩一过这个项目就再也没被打开过。这篇文章我不想做那种“贴一堆代码然后让你自己看”的教程而是把从 IDEA 2023 里从零创建 JavaWeb 项目、设计 MySQL 表结构、写购物车和订单核心链路到最后把项目发布到 Windows Server 上的 Apache Tomcat 这套完整流程里真正决定成败的细节梳理一遍。适合刚学完 JavaSE、正在啃 JavaWeb 的同学也适合打算拿商城当毕业设计题目的人参考。1. 为什么“javaweb购物商城”是最值得手敲一遍的练手项目1.1 一个商城项目覆盖了 JavaWeb 八成的核心知识点很多人问过我同一个问题JavaWeb 阶段做什么项目性价比最高我的答案一直是商城没有之一。你别看它业务简单就是登录注册、商品展示、加入购物车、下单这几件事但它几乎把 JavaWeb 的核心知识点全部串起来了Servlet 的生命周期和请求响应模型JSP、EL 表达式和 JSTL 标签库JDBC 连接数据库、事务控制Filter 过滤器做登录拦截、统一编码Session 和 Cookie 做会话状态管理分页查询、文件上传、MVC 分层思想这些东西如果分开学每个听起来都很抽象。但放进商城里它们的职责立刻变得具体Session 用来记住“谁登录了”Filter 用来挡住“没登录就想进购物车的人”事务用来保证“下单时订单和库存不会一个成功一个失败”。学完商城你再看网上那些同为 JavaWeb 的完整案例基本都能看懂套路。1.2 先手写 Servlet再上框架事半功倍网上动不动就有人找各种 JavaWeb 笔记的电子版包括黑马程序员风格的那套资料。可以看但我强烈建议先别急着学 Spring Boot。现在很多同学的路径是反的JavaWeb 还没搞明白直接跳到 Spring Boot 写商城结果遇到问题时完全不知道底层发生了什么。你手写过一遍原生的 Servlet JSP JDBC 商城之后再去用 Spring Boot 的任何框架感受完全不一样。你会明白RestController背后其实就是 ServletMyBatis 的SqlSession背后也还是 JDBC 的Connection。别人把框架当黑盒你当透明的玩具这种优势在面试时一问就能体现出来。1.3 它是最容易建立“成果感”的项目JavaSE 阶段你写管理系统的控制台程序成就感基本为零因为那只是打印几行字。但商城不一样每完成一个模块你都能在浏览器里真实地看到效果注册登录跳转、分页翻页、购物车角标数字变化、下单后订单出现在列表里。这种肉眼可见的反馈对新手来说太重要了它是你坚持把项目写完的最大动力。2. 2023版IDEA里创建JavaWeb项目卡住的人比想象中多2.1 新版项目模板的入口和老教程对不上很多人在 IDEA 2023 里第一步就卡住了照着旧教程找 Java Enterprise怎么找都找不到。这不是你的问题是 IDE 改版导致的。新版本里创建 JavaWeb 项目的入口变成了 Jakarta EEFile → New → Project左侧选择 Jakarta EE右侧选择 Web Application在 Application Server 里选择你本地的 Tomcat 版本SDK 选 17 或 21如果没有 Jakarta EE 这个选项说明你安装 IDEA 时没有勾选 Web 开发插件去插件市场装一下即可这里有一个非常隐蔽但高频的坑Java 包名从javax.servlet变成了jakarta.servlet。如果你用的是 Tomcat 10 及以上版本代码里必须写import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.*;但网上大量教程还是基于 Tomcat 9 写的里面全是javax.servlet。你直接复制过来IDEA 满屏红色报错然后你就开始怀疑是不是自己环境装错了。其实都没错就是 Tomcat 大版本升级换了命名空间而已。我个人的建议是新手先用 Tomcat 9 配 JDK 8 或 11网上资料多教程大多兼容等熟悉了再换 Tomcat 10 不迟。2.2 别忘了把 servlet-api.jar 加进依赖新建出来的 Web 项目是空壳你写 Servlet 类时 IDEA 会提示找不到HttpServlet。这是因为 tomcat 的 servlet-api.jar 还没加进项目的 classpath。正确做法去 Tomcat 安装目录下的lib文件夹里找到servlet-api.jarTomcat 10 是jakarta.servlet-api-*.jar然后在 IDEA 里 Project Structure → Libraries → 加号 → Java把 jar 选进来。虽然 IDEA 配置 Tomcat 后运行时会自动带上这个 jar但编译期不配置的话代码会一直报错很影响心情。2.3 配置运行环境时Application Context 最容易出错在右上角 Add Configuration → 加号 → Tomcat Server → Local选好 Tomcat 目录后重点来了。在 Deployment 页签里把项目的 war exploded 添加进去然后修改 Application Context。IDEA 默认会给你生成一个很长的上下文路径比如/javaweb_shop_war_exploded。我的做法是统一改成一个简洁的/shop。因为后面你在浏览器里访问的 URL 是http://localhost:8080/shop/xxx如果上下文路径和你想的不一致页面全部 404新手很容易在这一步放弃。配好后先写一个最简单的 Servlet 测试环境WebServlet(/hello) public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(text/html;charsetUTF-8); resp.getWriter().write(Hello JavaWeb); } }浏览器访问http://localhost:8080/shop/hello看到 Hello JavaWeb说明你的整套开发环境彻底跑通了。3. 数据库与购物车商城最核心的“状态”设计3.1 五张核心表一个都不能少商城项目的数据模型是所有功能的基础。我一般建议用五张表字段不需要太多但关系必须清晰表名核心字段作用tb_userid, username, password, nickname, phone用户信息与登录凭证tb_categoryid, name, sort商品分类tb_productid, name, price, stock, image, category_id商品信息与库存tb_orderid, order_no, user_id, total_amount, status, create_time订单主表tb_order_itemid, order_id, product_id, product_name, price, quantity订单明细表这里有两个特别想强调的设计细节。第一个是订单明细表里必须冗余商品名称和价格快照。你可以把订单明细里的 product_name 理解成“购物小票上打印的单价”而 tb_product 里的 price 是“货架上的现价”。商品改价了已经下单的订单不能跟着变所以要在下单那一刻把商品快照存进明细表。第二个是库存字段放在商品表里下单扣减时要配合事务使用后面第 4 章细说。3.2 连接池选 Druid别再用 DriverManager很多初学者写 JDBC 都是每次请求来了才DriverManager.getConnection()用完就关。本地跑一两个用户完全没感觉但一旦部署上线稍微有点并发数据库连接创建和销毁的开销就会拖垮系统。这就是为什么必须用连接池。商城项目里我推荐阿里的 Druid配置非常简单。在src/main/resources下建一个db.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai usernameroot password你自己的密码 initialSize5 maxActive20注意 MySQL 8.x 的驱动类名是com.mysql.cj.jdbc.Driver和 5.x 的com.mysql.jdbc.Driver不一样连接串里还必须加serverTimezoneAsia/Shanghai否则会报时区错误。代码里通过工厂拿连接Properties props new Properties(); props.load(new FileInputStream(db.properties)); DataSource dataSource DruidDataSourceFactory.createDataSource(props); Connection conn dataSource.getConnection();把 DataSource 放在一个DBUtil工具类里统一提供整个项目都从它获取连接后面改连接池参数只需要改配置文件。3.3 购物车放 Session 还是数据库练手阶段先放 Session购物车是商城状态设计里最典型的权衡问题。练手阶段我推荐直接用 Session 存购物车数据结构是MapInteger, CartItemkey 是商品 idvalue 是购物车条目对象MapInteger, CartItem cart (MapInteger, CartItem) session.getAttribute(cart); if (cart null) { cart new HashMap(); session.setAttribute(cart, cart); }加入购物车时如果 key 已经存在只需把数量加 1不存在则新建条目。这样实现快逻辑直观。但你必须知道它的缺陷Session 是存在服务器内存里的用户关闭浏览器、Session 过期、换一台设备购物车就全没了。真实商城购物车都会落库建一张 tb_cart_item字段就是 user_id、product_id、quantity甚至可以支持未登录用户用 Cookie 存临时的购物车数据。我建议你先用 Session 把整套流程跑通跑通后再把购物车迁移到数据库表你会更理解为什么要这样做——因为你会亲身体会到 Session 方案的痛。3.4 登录状态与 Filter 拦截登录成功后把用户对象放进 Sessionsession.setAttribute(loginUser, user);然后在/cart/*、/order/*这类需要登录才能访问的路径上挂一个 Filter。Filter 的逻辑非常简单从 Session 里取 loginUser取不到就重定向到登录页。这个设计让权限控制集中在一处而不是每个 Servlet 里重复写判断代码。4. 从商品列表到订单提交核心链路代码骨架4.1 首页商品分页最容易被忽略的 SQL商品列表这个功能看着简单但分页的 SQL 很容易写错。正确的写法是SELECT * FROM tb_product ORDER BY id DESC LIMIT ?, ?;第一个问号是起始位置第二个是每页数量。假设每页显示 12 个商品查第 3 页时起始位置就是(3-1)*1224。前端页面用 JSTL 做循环渲染页码通过?page3传过来。除了 SQL分页还要把总记录数查出来这样才能算出一共有多少页不然页码条没法渲染。4.2 加入购物车的完整逻辑加购的 Servlet 核心代码并不复杂关键是处理好“购物车尚未初始化”和“商品已存在”两种边界情况。参考实现如下WebServlet(/cart/add) public class CartAddServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { int productId Integer.parseInt(req.getParameter(productId)); HttpSession session req.getSession(); MapInteger, CartItem cart (MapInteger, CartItem) session.getAttribute(cart); if (cart null) { cart new HashMap(); session.setAttribute(cart, cart); } CartItem item cart.get(productId); if (item null) { item new CartItem(); item.setProductId(productId); item.setQuantity(1); cart.put(productId, item); } else { item.setQuantity(item.getQuantity() 1); } resp.sendRedirect(req.getContextPath() /cart/list); } }这里有一个细节重定向时一定要用req.getContextPath()拼接地址不要写死成/shop/cart/list。因为你的项目可能以后换名字部署或者别的地方上下文路径不同写死就 404加上 contextPath 动态获取才是正确姿势。4.3 下单事务库存扣减必须防超卖提交订单是整个项目里最需要认真对待的环节。一次下单涉及三件事写入订单主表、写入订单明细表、扣减库存。这三步必须放在同一个数据库事务里否则就会出现“订单建好了但库存没扣”这种严重的数据不一致。核心事务代码骨架Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 1. 插入订单主表获取自增主键 orderId // 2. 遍历购物车逐一插入订单明细表累加 totalAmount // 3. 扣减库存SQL 必须带上库存充足的判断 String sql UPDATE tb_product SET stock stock - 1 WHERE id ? AND stock 1; // 如果受影响行数为 0说明库存不足直接抛异常回滚 conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); }第三步的那个AND stock 1特别重要。很多第一次做商城的人会写成先查库存大于 0 再执行 update。这种“先查后改”的方式在并发场景下会超卖——两个人同时读到库存 1都认为可以买结果都执行了扣减库存变成负数。在 update 语句里带上库存条件数据库会在行锁级别帮你判断这才是正确的防超卖姿势本质是乐观锁的思想。4.4 订单号生成与支付模块的简化方案订单号不要用数据库自增 id否则既不好看又容易被猜到销量。练手阶段用时间加用户 id 再拼一段随机数就够String orderNo new SimpleDateFormat(yyyyMMddHHmmss).format(new Date()) userId String.format(%04d, new Random().nextInt(10000));真实系统中会使用雪花算法等全局唯一 ID 生成方案但商城练手项目用这种简单方式没有问题。支付模块我强烈建议用模拟支付下单后订单状态直接变成“已支付”或“待发货”不要真的接第三方支付接口。因为学生项目接真实支付既需要商户资质又存在资金风险完全没有必要。把状态机设计好后面想接随时可以接。5. 发布到Windows ServerApache与Tomcat的配合方式5.1 为什么前面非要套一个 Apache很多新手不理解Tomcat 自己就能跑为什么还要在 Windows Server 上装 Apache我的经验是主要有三个实际原因80 端口的问题Tomcat 默认监听 8080用户访问还得敲端口号体验很差。但 80 端口通常被 IIS 或者其他 Web 服务占着Apache 可以直接占用 80。静态资源处理Apache 处理图片、CSS、JS 这类静态文件比 Tomcat 更高效可以把静态请求拦截下来只有动态请求才转发给 Tomcat。统一入口以后要配置 HTTPS 证书、做多站点虚拟主机、做负载均衡都放在 Apache 这一层比直接改 Tomcat 更灵活。5.2 部署的完整操作步骤一套比较顺利的部署流程是这样的在服务器上安装 JDK配置环境变量JAVA_HOME和PATH解压 Tomcat 到某个目录配置CATALINA_HOME在 IDEA 里把项目打成 war 包Build → Build Artifacts → 选择xxx:war→ Build把 war 包复制到 Tomcat 的webapps目录启动 Tomcat 会自动解压先访问http://服务器IP:8080/shop确认项目正常安装 Apache Httpd改配置让 80 端口接收请求并转发到 Tomcat关键点在第六步。Apache 转发到 Tomcat 有两种主流方式mod_proxy_http直接转发 HTTP 协议或者mod_proxy_ajp走 AJP 协议。如果是中小型项目直接用第一种最省事Apache 配置如下LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so VirtualHost *:80 ServerName www.example.com ProxyPass /shop http://127.0.0.1:8080/shop ProxyPassReverse /shop http://127.0.0.1:8080/shop /VirtualHost如果你要用 AJP注意 Tomcat 9 默认把 AJP Connector 注释掉了需要去server.xml里手动打开 8009 端口的监听再把上面的proxy_http换成proxy_ajp。很多教程直接让你配 AJP 但没说这一步导致配完一直 502问题就出在这里。5.3 发布阶段最容易撞上的几个故障端口被占用是 Windows Server 上最常见的问题尤其是 80 端口被 IIS 占住。用netstat -ano | findstr :80找到 PID再去任务管理器结束进程或者干脆把 IIS 服务停掉。Tomcat 的 8080 端口被占也是一样的排查逻辑。启动 Tomcat 后 windows 防火墙会弹窗记得勾选允许访问否则服务器外部访问不到 8080 或 80 端口。项目起不来时第一时间看tomcat/logs/目录下的日志文件比如catalina.日期.log后端异常全在那里比瞎猜高效得多。6. 这半年我替别人排查过最多的几个JavaWeb坑6.1 中文乱码一个坑里藏着三层问题中文乱码是 JavaWeb 项目里出现过最多次的问题而且经常是三处同时出问题你只修一处根本没用。第一层是响应乱码Servlet 返回中文时必须在输出之前设置resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8);第二层是请求乱码。POST 请求可以在 Servlet 里用req.setCharacterEncoding(UTF-8)但每个 Servlet 都写太麻烦最好是写一个 CharacterEncodingFilter 统一处理WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); chain.doFilter(req, resp); } }第三层是数据库乱码。连接串里已经写了characterEncodingutf8还不够MySQL 建表时也要确认表本身是utf8mb4编码。三层都对齐中文才彻底不会有问题。JSP 页面头部还要记得写% page contentTypetext/html;charsetUTF-8 %。6.2 一切 404先查 Context Path再查 web.xml本地开发时 404 最常见的原因不是代码写错而是 IDEA 里 Artifact 的 Application Context 配置和 URL 对不上。你访问的是/shop/hello实际部署上下文却是/javaweb_shop_war_exploded/hello那必然 404。排查思路很简单先看 IDEA 底部 Tomcat 启动日志里打印的上下文路径再对比浏览器的地址栏。另一个 404 容易忽略的地方是 web.xml 的welcome-file-list。如果你的首页是index.jsp而文件实际放在WEB-INF目录下那是无论如何都访问不到的因为WEB-INF目录对浏览器不可见。页面文件要放在webapp目录下而不是WEB-INF里。6.3 登录接口千万别用字符串拼接 SQL我亲眼见过某个同学的登录接口用字符串拼接 SQLString sql SELECT * FROM tb_user WHERE username username AND password password ;然后他测试时在用户名框里输入 or 11密码随便填直接登录成功。这就是典型的 SQL 注入危害远不止进后台看看一旦别人拿到你数据库的任何表后果不堪设想。正确写法是 PreparedStatement 占位符String sql SELECT * FROM tb_user WHERE username? AND password?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password);这个习惯必须从一开始就养成不要觉得“我项目小没人攻击”这个习惯的价值会在你以后任何项目里体现。6.4 密码存储和明文说再见在商城这类项目里数据库表里的密码即使是开发阶段也建议做一次哈希存储不要直接放明文。练手阶段用MD5加盐已经能说明你有安全意识String salt shop_salt; String encoded DigestUtils.md5Hex(password salt);再进阶一点用 BCrypt每次生成带随机盐的哈希安全性更好。加了这个细节之后至少你可以理直气壮地在项目文档里写一条“对用户密码进行了加密处理不存储明文”。这种小细节到了答辩和面试阶段都是加分项。带过那么多同学做完这个商城项目后我发现一个规律凡是老老实实把登录、分页、购物车、下单、部署这一整套流程亲手走通的人后面学框架、找工作都会顺利很多。所以这项目别看它老值得花两周时间好好敲一遍每个坑都是你往后排查问题的宝贵经验。本文还有配套的精品资源点击获取
返回列表