JSP网上购物系统开发实战:JavaBean、Servlet与JSP整合要点
2026/9/23 20:33:04 网站建设 项目流程

简介:面向Java Web初学者的学校实训项目,整合JavaBean、Servlet与JSP三大核心技术,完整实现网上购物系统,可有效解决毕业生实训或课程设计中的项目落地问题。压缩包共179个文件,包含50个Java源码与50个class文件、29个JSP页面、8个TLD标签库描述文件,以及数据库脚本、jar依赖、PDF/PPT说明文档等,整体仅7.52MB,目录结构清晰,便于按功能模块检索学习。已有1187人学习下载,内容覆盖MVC设计模式、JDBC数据访问、Session购物车、异常处理与安全防护等关键知识点,并配有页面截图与图片资源,适合毕业实训、课设参考和JavaWeb入门者。通过此项目可掌握从DAO到Servlet控制层再到JSP视图层的完整开发流程,理解电商系统业务逻辑与代码组织方式,同时熟悉web.xml配置、标签库使用及数据库交互方法,是一份高性价比的实战学习资料。

1. 学校实训 JSP 项目:网上购物系统的 JavaBean+Servlet 组合

很多人第一次拿到“网上购物系统”这个 JSP 实训项目时,会以为重点是购物车和商品展示,但真正决定成绩的是 JavaBean、Servlet、JSP 三者的分工。JavaBean 用来封装商品和用户的数据,Servlet 接收浏览器请求并控制页面跳转,JSP 负责把数据渲染成 HTML。这个结构听起来简单,但大部分运行失败的项目,问题都出在 Servlet 配置、session 使用和 JSP 里的类型转换上。下面直接以这个项目为对象,把环境搭建、核心代码、购物车会话和答辩前的优化一次讲完。如果你正在做这个实训项目,按照这里的方式把请求路径走一遍,比死记代码更有用。

2. 网上购物系统的运行环境搭建与 Eclipse 导入步骤

2.1 拿到项目 zip 后,先检查目录结构而不是直接启动

拿到的压缩包数字节很小,但里面往往同时包含完整 Eclipse 工程和一份数据库脚本。我一般不会急着解压到桌面,而是先建一个纯英文目录,比如D:\work\shop,避免中文路径导致 Tomcat 解析文件失败。解压后先看有没有.project.classpath文件,这能直接区分它是 Eclipse 导出工程还是纯源码包。

常见的网上购物系统目录结构类似这样:

shop/ ├── src/ │ ├── com/shop/bean/ # JavaBean 实体类 │ ├── com/shop/servlet/ # Servlet 控制器 │ └── com/shop/util/ # 数据库连接工具 ├── WebContent/ │ ├── jsp/ │ │ ├── login.jsp │ │ ├── register.jsp │ │ ├── index.jsp │ │ └── cart.jsp │ ├── WEB-INF/ │ │ ├── web.xml │ │ └── lib/ │ └── css/ js/ images/ └── .project

这个结构里,src下是 Java 源码,WebContent是 Web 根目录。JSP 页面在运行时会由 Tomcat 编译成 servlet class,但源 Java 文件必须在src下,否则无法被容器找到。如果 zip 里没有.project文件,就不能用 Eclipse 的“Existing Projects into Workspace”导入,需要新建一个 Dynamic Web Project,再把WebContentsrc内容拷进去。

2.2 确认 JDK、Tomcat 和 Servlet 版本兼容

这个项目基于 JSP 2.3 和 Servlet 3.1 左右,所以常见的搭配是 JDK 8 + Tomcat 8.5。Tomcat 10 不建议直接使用,因为它的包名已经换成jakarta.servlet,旧代码里的import javax.servlet.http.HttpServlet会在运行时直接报ClassNotFoundException。在命令行里查看当前 JDK 版本:

java -version

如果显示的不是 1.8 或 11,需要到环境变量里调整JAVA_HOME。Eclipse 中还需要在项目属性里指定 Targeted Runtime,否则javax.servlet.http.HttpServlet这个类找不到,编译会报错。这里给出一个参考兼容表:

项目依赖推荐版本说明
JDK1.8 或 11网上购物系统不涉及较新的语法,8 足够
Tomcat8.5 / 9.0避免 Tomcat 10 的 Jakarta 命名空间问题
Servlet API3.1对应javax.servlet
JSP API2.3Tomcat 自带,不需要额外引入
MySQL Connector5.1.x 或 8.0.x注意驱动类名不同

这里要特别提醒,不要自己下载一个 servlet-api.jar 放到WEB-INF/lib下,因为 Tomcat 已经内置了同名类,目录重了会冲突,轻则启动失败,重则造成诡异的重写方法错误。

2.3 用 Eclipse 导入项目并启动 Tomcat

导入步骤其实很固定,我一般按这个顺序操作:

  1. 打开 Eclipse,选择 File → Import → General → Existing Projects into Workspace。
  2. Select root directory 指向D:\work\shop,如果识别成功,Projects 列表会出现项目名。
  3. 如果导入后 Java 类报错,右键项目 → Properties → Targeted Runtimes,勾选 Tomcat。
  4. 右键项目 → Run As → Run on Server,选择 Tomcat 9,完成启动。
  5. 浏览器打开http://localhost:8080/shop/,看到首页就说明环境配通了。

如果启动时出现端口占用,可以去 Tomcat 的server.xml中把 Connector port 改成 8081 或 8082。访问地址也要同步带端口。Tomcat 控制台出现一行Server startup in [xxx] milliseconds才代表部署成功,不要只看页面没反应就反复重启。

2.4 JSP 编译后的 class 文件与找不到主类排错

实训中出现“错误: 找不到或无法加载主类 org.jeecg.JeecgSystemApplication”这类信息,通常是因为用普通 Java 应用方式启动了一个 Spring Boot 类,而当前项目不是 Spring Boot 工程。网上购物系统不存在 main 方法入口,它必须依托 Tomcat 运行。JSP 编译后的 class 文件实际存放在 Tomcat 的work/Catalina/localhost/项目名/org/apache/jsp/目录下,如果你改动 JSP 后没看到效果,可以先删除整个work目录再重启 Tomcat,强制重新编译。

另一个常见错误是 Tomcat 报ClassNotFoundException: javax.servlet.http.HttpServlet,这代表项目 Build Path 里缺少 Servlet 容器依赖。解决方式是在项目属性里勾选 Targeted Runtimes,或者在 Java Build Path 的 Libraries 中点击 Add Library → Server Runtime → Tomcat,而不是去网上下载 jar 包。环境配置到这里,后面写 JavaBean 和 Servlet 时就不会被这些基础问题打断。

3. JavaBean + Servlet 如何驱动网上购物系统的数据流

3.1 JavaBean 不只是 getter/setter,还承担数据校验

网上购物系统里最典型的 JavaBean 有 User、Product、CartItem 和 Cart。很多学生只写字段和 getter/setter,但这样写唯一的作用是当容器传输数据的工具。实训评分时,一个能体现工程能力的做法是在 JavaBean 中加入简单的业务校验,比如价格不能为负、库存不能小于零。下面是一个 Product 的典型写法:

package com.shop.bean; public class Product { private int id; private String name; private double price; private String image; private String description; private int stock; public Product() {} public Product(int id, String name, double price) { this.id = id; this.name = name; this.price = price; } public double getPrice() { return price; } public void setPrice(double price) { if (price < 0) { throw new IllegalArgumentException("价格不能为负数"); } this.price = price; } // 其他 getter/setter 省略 }

这段代码里最关键的是必须保留无参构造方法,因为 JSP 的<jsp:useBean>标签通过反射创建对象时会调用无参构造。如果只写了带参构造,页面运行时会抛InstantiationExceptionsetPrice中加了一个简单的金额校验,这不算复杂,但能在答辩时展示你已经理解了 JavaBean 不只是数据仓库,它还可以约束数据的合法性。

3.2 Servlet 的 doGet/doPost 分工与登录流程

网上购物系统中,Servlet 是唯一负责接收请求的入口。以登录为例,用户从login.jsp提交表单到 LoginServlet,LoginServlet 再调用数据库工具类找到 User 对象,然后把结果放到 session 或 request 中。这个流程看着简单,但 doGet 和 doPost 的分工很容易搞混。

package com.shop.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import com.shop.bean.User; import com.shop.util.DBUtil; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); User user = DBUtil.findUser(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user); response.sendRedirect(request.getContextPath() + "/index.jsp"); } else { request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("/jsp/login.jsp").forward(request, response); } } }

登录失败用 forward 转发,登录成功用 sendRedirect 重定向。这样做的原因是:重定向会让浏览器地址栏发生跳转,用户刷新页面时不会重复提交登录表单;forward 则保留原来的请求地址,登录失败后刷新页面会再次提交表单,这是很多购物系统出现重复数据的原因之一。

3.3 用 web.xml 或注解配置 Servlet 映射

上面代码里使用了@WebServlet("/login"),这是 Servlet 3.0 之后提供的注解方式,不需要在 web.xml 中维护映射关系。但很多学校实训项目还是保留旧式 web.xml 配置,因为考试环境可能用老版教材。下面给出同一份映射的 web.xml 写法:

<servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>com.shop.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>

这两种配置方式不能同时存在,否则同一个 url-pattern 会被解析成两个 servlet,启动时可能不会报错,但实际请求时容易走到错误类。下面整理一张网上购物系统的常见 URL 映射表,方便在调试时对照:

请求路径Servlet 类作用
/loginLoginServlet用户登录
/registerRegisterServlet用户注册
/productProductServlet商品列表与详情
/cartCartServlet添加购物车、修改数量
/orderOrderServlet提交订单

这张表就是整个系统的坐标。无论页面怎么跳转,最后落脚点都是这些 Servlet 对应的方法,所以调试时可以先把每个 URL 直接访问一遍,看 Servlet 是否正常响应。

3.4 数据从 Servlet 传到 JSP 的三种作用域

Servlet 拿到数据后,需要把数据交给 JSP 展示。网上购物系统里常用 request、session、application 三种作用域。商品列表适合放在 request 中,因为每次刷新都会重新查询数据库;登录用户信息必须放在 session 中,因为多个页面都要读取;购物车也建议放在 session 中,因为它是用户在会话期间的持久状态。下面这段 JSP 代码展示了如何取出商品列表:

<%@ page contentType="text/html; charset=UTF-8" %> <%@ page import="java.util.List, com.shop.bean.Product" %> <% List<Product> products = (List<Product>) request.getAttribute("productList"); for (Product p : products) { %> <div class="product-item"> <h3><%= p.getName() %></h3> <p>价格:<%= p.getPrice() %></p> <a href="cart?productId=<%= p.getId() %>">加入购物车</a> </div> <% } %>

这段代码有两个容易踩坑的点。第一个是强转之前必须判断request.getAttribute("productList")是否为 null,否则 Servlet 直接把页面转发回来时,这里会抛 NullPointerException。第二个是<a>标签里的路径最好加上${pageContext.request.contextPath},也就是写成项目根路径开头,否则在带项目名的部署环境下会 404。

4. JSP 页面渲染、Session 会话与购物车实现

4.1 用 jsp:useBean 简化个人信息展示页

网上购物系统的用户中心页通常要展示当前登录用户的昵称、邮箱、电话等信息。如果用 scriptlet 写太多 Java 代码,页面会显得很笨重。常见的简化写法是用<jsp:useBean>拿到 session 中的用户对象,再通过<jsp:getProperty>输出属性值:

<jsp:useBean id="user" scope="session" class="com.shop.bean.User" /> <h2>欢迎,<jsp:getProperty name="user" property="nickname" /></h2> <p>邮箱:<jsp:getProperty name="user" property="email" /></p>

这里scope="session"会先查找 session 里 key 为 user 的对象,如果找不到才会自动 new 一个 User。因此在 Servlet 里session.setAttribute("user", user)的 key 必须和这里的 id 一致,否则页面会输出 null 或者空对象。对“jsp个人信息展示页面”这个搜索意图来说,这样写既简洁又不会出现 scriptlet 的强制类型转换问题。

4.2 购物车的 JavaBean 设计与会话存储

购物车实体类通常有两个:CartItem 表示一行商品,Cart 表示整个购物车。Cart 持有List<CartItem>,并提供 add、remove、update 方法。这里给一个简化版:

package com.shop.bean; import java.util.ArrayList; import java.util.List; public class Cart { private List<CartItem> items = new ArrayList<>(); public void add(CartItem item) { for (CartItem i : items) { if (i.getProductId() == item.getProductId()) { i.setQuantity(i.getQuantity() + item.getQuantity()); return; } } items.add(item); } public double getTotalPrice() { double total = 0; for (CartItem item : items) { total += item.getProduct().getPrice() * item.getQuantity(); } return total; } public List<CartItem> getItems() { return items; } public int getItemCount() { int count = 0; for (CartItem item : items) { count += item.getQuantity(); } return count; } }

这个类的关键是add方法里先判断商品是否已经存在,如果存在就只增加数量而不是重复添加一行。这个逻辑在实训项目里很常见,但要提醒的是getTotalPrice依赖item.getProduct().getPrice(),如果 Product 对象没有被完整赋值,会出现默认 0.0 的价格,购物车总金额变成 0。在 Servlet 中添加购物车时,要确保把完整的商品信息从数据库查出来再放入 CartItem。

Cart 对象放在 session 中,存取代码如下:

Cart cart = (Cart) session.getAttribute("cart"); if (cart == null) { cart = new Cart(); session.setAttribute("cart", cart); } cart.add(cartItem);

这里不能把 cart 放到 request 中,因为 request 的生命周期只覆盖一次请求,用户跳到购物车页面后对象就被回收了。session 默认保持 30 分钟,足够覆盖一次完整的浏览和下单过程。

4.3 购物车列表页面的数量修改与 JSP 表单

购物车页面的核心是允许用户修改数量,下面是一个标准的 JSP 输出片段:

<%@ page import="com.shop.bean.Cart, com.shop.bean.CartItem" %> <% Cart cart = (Cart) session.getAttribute("cart"); if (cart == null) { out.print("<p>购物车为空</p>"); return; } %> <table border="1"> <tr><th>商品名</th><th>单价</th><th>数量</th><th>小计</th></tr> <% for (CartItem item : cart.getItems()) { %> <tr> <td><%= item.getProduct().getName() %></td> <td>¥<%= item.getProduct().getPrice() %></td> <td> <form action="cart" method="post"> <input type="hidden" name="productId" value="<%= item.getProductId() %>" /> <input type="number" name="quantity" value="<%= item.getQuantity() %>" min="1" /> <input type="submit" value="更新" /> </form> </td> <td>¥<%= item.getProduct().getPrice() * item.getQuantity() %></td> </tr> <% } %> </table> <p>总金额:¥<%= cart.getTotalPrice() %></p>

这个页面的坑在于action="cart",如果页面位于/jsp/cart.jsp,相对路径会解析成/jsp/cart,从而 404。正确写法是<%= request.getContextPath() %>/cart,或者使用${pageContext.request.contextPath}/cart。在 CartServlet 的 doPost 中,也要注意quantity若为空或负数,需要回退成 1,否则会在数量计算时产生负数库存。

4.4 屏蔽 JSP 离开页面提示与防止重复提交

演示项目时,浏览器底部经常弹出“离开此页面后数据不会被保存”的提示,非常影响观感。这个提示来自表单页面的beforeunload事件,可以通过一行脚本关闭:

window.onbeforeunload = null;

但要注意,这行代码并不能解决真正的重复提交问题。网上购物系统的下单按钮如果被连续点击,会产生多个相同订单。实训阶段最稳妥的防重方案是使用一次性令牌:JSP 表单加一个<input type="hidden" name="token" value="<%= session.getAttribute("orderToken") %>" />,Servlet 收到请求后先比对 session 中的 token,如果一致就执行下单并清空 token,如果不一致就拒绝。这种方式比单纯屏蔽离开页面提示更值得写到答辩说明里。

JSP 页面还可以用<%@ include file="/common/header.jsp" %>抽取公共导航栏。静态 include 在编译阶段把内容插入当前页面,所以公共变量的作用域会扩展到主页面;<jsp:include>则是运行时的动态包含,适合更换频率更高的区域。网上购物系统的导航栏和页脚一般用静态 include 就够了,既能减少重复代码,也不会引入额外的请求延迟。

5. 答辩前必做的 JSP 项目优化:编码、JSTL 与错误排查

5.1 用 Filter 统一处理编码和登录校验

很多实训项目在提交表单后出现中文乱码,原因只在一处 Servlet 里设置了编码,其他请求没设置。与其在每个 Servlet 里写request.setCharacterEncoding("UTF-8"),不如用一个 Filter 统一处理:

package com.shop.filter; import java.io.IOException; import javax.servlet.*; public class EncodingFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } }

Filter 减轻了 Servlet 的重复代码,也能顺便处理登录校验。比如只拦截/cart/order这些需要登录的路径。注意放行登录和注册接口,否则会出现“重定向次数过多”的死循环。这个 Filter 是答辩时非常容易讲清楚的一个加分点,因为几乎所有 JSP 项目都存在中文乱码问题。

5.2 用 JSTL 替换 scriptlet,减少页面里的 Java 代码

网上购物系统的 JSP 页面如果满屏都是<% %>,代码可读性会很差。最直接的优化是把循环和 if 判断换成 JSTL。在 JSP 头部引入标签库:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

然后用<c:forEach>循环商品列表:

<c:forEach var="p" items="${productList}"> <div class="product-item"> <h3>${p.name}</h3> <p>价格:${p.price}</p> <a href="${pageContext.request.contextPath}/cart?productId=${p.id}">加入购物车</a> </div> </c:forEach>

JSTL 的items="${productList}"底层会通过反射调用 Product 的 getter,所以 JavaBean 中必须有getName()getPrice()getId()。如果页面里还需要输出 JSON 数据给前端,建议在 Java 类里用 JSONArray 构建好字符串,再通过${jsonList}输出到 JSP,避免在页面手写字符串拼接导致引号转义错误。

5.3 答辩前快速验证的常见错误清单

现象检查点
中文乱码每个 Servlet 是否在读取参数前设置 UTF-8,数据库 URL 是否带字符集参数
404 找不到页面URL 是否包含项目名,web.xml welcome-file 是否正确
页面显示 nulljsp:useBean 的 id 是否与 session.setAttribute 的 key 一致
订单重复创建是否有防重复提交令牌,没有则加 hidden token
修改 JSP 不生效删除 Tomcat work 目录后重启,再刷新浏览器

答辩前按这张表把所有页面都过一遍,会节省大量现场调试时间。

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

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

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

立即咨询