回顾一下之前用JSP做的一个项目,关于 Web 开发学习中,没有什么比亲手完成一个完整项目更能巩固知识的了。电商场景是我们经常说的,在线购物系统作为经典的 Web 应用场景,涵盖了用户管理、商品展示、购物流程、订单处理等核心模块,几乎能用到所有 JSP 相关技术。本文将带你从需求分析开始,一步步设计并实现一个功能完整的在线购物系统,让你在实战中掌握 JSP、Servlet、JSTL、过滤器、监听器等技术的综合应用。

一、项目需求分析

开发项目的第一步不是写代码,而是搞清楚 “要做什么”。在线购物系统的核心是 “让用户能在线浏览商品、下单购买”,围绕这个核心,我们梳理出以下需求:

1. 核心功能模块

模块 主要功能
用户模块 注册(用户名、密码、手机号等)、登录、个人信息修改、密码重置
商品模块 商品列表展示(分页)、商品详情、按分类 / 关键词搜索、商品分类浏览
购物车模块 添加商品(支持数量)、修改商品数量、删除商品、清空购物车、结算
订单模块 生成订单(从购物车)、订单列表查询、订单详情、订单状态更新(付款 / 发货等)

2. 非功能需求

  • 性能:商品列表页面加载时间≤2 秒,支持同时 100 人在线访问;
  • 安全:密码加密存储,防 SQL 注入和 XSS 攻击,登录状态安全管理;
  • 易用性:操作流程简洁(浏览→加购→结算→支付),页面响应及时。

3. 用户角色与流程

系统只有普通用户一种角色(简化版,暂不做管理员模块),核心流程:

用户注册→登录→浏览商品→加入购物车→结算→生成订单→查看订单

二、数据库设计

数据是系统的血液,合理的数据库设计是项目成功的基础。根据需求,我们设计 5 张核心表,用 MySQL 实现。

1. 表结构设计

① 用户表(t_user)

存储用户基本信息,密码需加密存储。

字段名 类型 约束 说明
id int PK, AUTO_INCREMENT 用户 ID(主键)
username varchar(50) NOT NULL, UNIQUE 用户名(唯一)
password varchar(100) NOT NULL 密码(BCrypt 加密)
phone varchar(20) UNIQUE 手机号(唯一,用于找回密码)
address varchar(200) 默认收货地址
create_time datetime NOT NULL 注册时间
② 商品分类表(t_category)

对商品进行分类(如电子产品、服装等)。

字段名 类型 约束 说明
id int PK, AUTO_INCREMENT 分类 ID
name varchar(50) NOT NULL 分类名称(如 “手机”)
sort int DEFAULT 0 排序(数字越小越靠前)
③ 商品表(t_product)

存储商品信息,关联分类表。

字段名 类型 约束 说明
id int PK, AUTO_INCREMENT 商品 ID
name varchar(100) NOT NULL 商品名称
price decimal(10,2) NOT NULL 商品单价
stock int NOT NULL, DEFAULT 0 库存数量
category_id int FK 所属分类 ID(关联 t_category)
image varchar(200) 商品图片路径
description text 商品描述
create_time datetime NOT NULL 上架时间
④ 购物车表(t_cart)

存储用户的购物车信息,关联用户和商品。

字段名 类型 约束 说明
id int PK, AUTO_INCREMENT 购物车项 ID
user_id int FK, NOT NULL 所属用户 ID(关联 t_user)
product_id int FK, NOT NULL 商品 ID(关联 t_product)
quantity int NOT NULL, DEFAULT 1 商品数量(至少 1)
update_time datetime NOT NULL 最后更新时间
UNIQUE KEY (user_id, product_id) 确保同一用户不会重复添加同一商品
⑤ 订单表(t_order)

存储订单主信息,关联用户。

字段名 类型 约束 说明
id varchar(50) PK 订单号(UUID 生成,如 “ORD20231005xxx”)
user_id int FK, NOT NULL 所属用户 ID
total_price decimal(10,2) NOT NULL 订单总金额
status int NOT NULL, DEFAULT 0 订单状态(0:待付款,1:已付款,2:已发货,3:已完成,4:已取消)
address varchar(200) NOT NULL 收货地址
phone varchar(20) NOT NULL 收货电话
create_time datetime NOT NULL 下单时间
⑥ 订单项表(t_order_item)

存储订单中的商品明细,关联订单和商品。

字段名 类型 约束 说明
id int PK, AUTO_INCREMENT 订单项 ID
order_id varchar(50) FK, NOT NULL 所属订单号(关联 t_order)
product_id int FK, NOT NULL 商品 ID
product_name varchar(100) NOT NULL 商品名称(下单时快照,避免商品改名)
product_price decimal(10,2) NOT NULL 商品单价(下单时快照)
quantity int NOT NULL 购买数量

2. ER 图与表关系

  • 用户与订单:1 对多(一个用户可有多笔订单);
  • 用户与购物车:1 对多(一个用户可有多条购物车项);
  • 分类与商品:1 对多(一个分类包含多个商品);
  • 订单与订单项:1 对多(一笔订单包含多个商品明细);
  • 商品与订单项:1 对多(一个商品可出现在多笔订单中)。

三、系统架构设计

采用MVC 分层架构,明确各层职责,便于后期维护和扩展。

1. 架构分层

职责 技术实现
视图层(View) 展示数据,接收用户输入 JSP、JSTL、EL、CSS、JavaScript
控制层(Controller) 接收请求,分发任务,响应结果 Servlet、过滤器(Filter)
业务层(Service) 处理业务逻辑(如登录验证、订单生成) Java 类(Service 接口 + 实现类)
数据访问层(DAO) 操作数据库(CRUD) JDBC、DBUtils(简化数据库操作)
模型(Model) 封装数据(如用户、商品信息) JavaBean(实体类)
工具层(Util) 提供通用功能(如数据库连接、加密) 工具类(如 DBHelper、PasswordUtils)

2. 核心技术选型

  • Web 容器:Tomcat 9.0;
  • 数据库:MySQL 8.0;
  • 构建工具:Maven(管理依赖);
  • 数据库连接池:HikariCP(高性能);
  • JSP 标签库:JSTL 1.2、自定义标签(如分页标签);
  • 前端技术:Bootstrap(简化 UI 开发)、jQuery(处理 DOM 和 AJAX)。

3. 项目目录结构

shopping/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   ├── com/
│   │   │   │   ├── shopping/
│   │   │   │   │   ├── controller/       // Servlet控制器
│   │   │   │   │   ├── service/          // 业务逻辑(接口+实现)
│   │   │   │   │   │   ├── impl/
│   │   │   │   │   ├── dao/              // 数据访问(接口+实现)
│   │   │   │   │   │   ├── impl/
│   │   │   │   │   ├── model/            // 实体类(JavaBean)
│   │   │   │   │   ├── filter/           // 过滤器(编码、权限)
│   │   │   │   │   ├── listener/         // 监听器(在线统计)
│   │   │   │   │   └── util/             // 工具类
│   ├── webapp/
│   │   ├── WEB-INF/
│   │   │   ├── classes/                  // 编译后的class文件(Maven自动生成)
│   │   │   ├── lib/                      // 依赖jar包(Maven自动管理)
│   │   │   ├── tags/                     // 自定义标签文件(如分页标签)
│   │   │   ├── tlds/                     // TLD文件
│   │   │   └── web.xml                   // 配置文件(Servlet、Filter等)
│   │   ├── static/                       // 静态资源
│   │   │   ├── css/
│   │   │   ├── js/
│   │   │   └── images/
│   │   ├── user/                         // 用户模块JSP
│   │   │   ├── login.jsp
│   │   │   ├── register.jsp
│   │   │   └── info.jsp
│   │   ├── product/                      // 商品模块JSP
│   │   │   ├── list.jsp
│   │   │   └── detail.jsp
│   │   ├── cart/                         // 购物车模块JSP
│   │   │   └── cart.jsp
│   │   ├── order/                        // 订单模块JSP
│   │   │   ├── list.jsp
│   │   │   └── detail.jsp
│   │   ├── index.jsp                     // 首页
│   │   └── error.jsp                     // 错误页
└── pom.xml                               // Maven配置文件

四、核心模块实现

我们按模块逐步实现,重点讲解关键功能的核心代码和技术要点。

1. 基础准备工作

① 数据库连接工具类

使用 HikariCP 连接池,封装获取连接的方法:

public class DBHelper {
    private static HikariDataSource dataSource;

    static {
        // 初始化连接池(从配置文件读取参数)
        Properties props = new Properties();
        try {
            props.load(DBHelper.class.getClassLoader().getResourceAsStream("db.properties"));
            HikariConfig config = new HikariConfig(props);
            dataSource = new HikariDataSource(config);
        } catch (IOException e) {
            throw new RuntimeException("数据库配置文件加载失败", e);
        }
    }

    // 获取连接
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    // 关闭资源
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
        if (stmt != null) try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
        if (conn != null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
    }
}

db.properties配置:

jdbcUrl=jdbc:mysql://localhost:3306/shopping?useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
driverClassName=com.mysql.cj.jdbc.Driver
maximumPoolSize=10
minimumIdle=5
② 字符编码过滤器

解决全站中文乱码问题:

@WebFilter("/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        chain.doFilter(request, response);
    }
    // init和destroy略
}

2. 用户模块:注册与登录

① 实体类(User.java)
public class User {
    private int id;
    private String username;
    private String password; // 存储加密后的密码
    private String phone;
    private String address;
    private Date createTime;

    // getter和setter方法
}
② 注册功能实现

流程:用户提交表单→后端验证(用户名是否存在、密码强度等)→密码加密→存入数据库。

注册 Servlet(RegisterServlet.java)

@WebServlet("/user/register")
public class RegisterServlet extends HttpServlet {
    private UserService userService = new UserServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String phone = req.getParameter("phone");
        String address = req.getParameter("address");

        // 前端验证(略,实际项目需JS验证非空、格式等)
        // 后端验证
        if (userService.existsUsername(username)) {
            req.setAttribute("error", "用户名已存在");
            req.getRequestDispatcher("/user/register.jsp").forward(req, resp);
            return;
        }

        // 密码加密(BCrypt)
        String encryptedPwd = PasswordUtils.encrypt(password);

        // 封装用户对象
        User user = new User();
        user.setUsername(username);
        user.setPassword(encryptedPwd);
        user.setPhone(phone);
        user.setAddress(address);
        user.setCreateTime(new Date());

        // 调用Service保存
        boolean success = userService.register(user);
        if (success) {
            // 注册成功,跳转到登录页
            resp.sendRedirect(req.getContextPath() + "/user/login.jsp?msg=注册成功,请登录");
        } else {
            req.setAttribute("error", "注册失败,请重试");
            req.getRequestDispatcher("/user/register.jsp").forward(req, resp);
        }
    }
}

注册页面(register.jsp)

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>注册</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2>用户注册</h2>
    <c:if test="${not empty error}">
        <div class="alert alert-danger">${error}</div>
    </c:if>
    <form action="${pageContext.request.contextPath}/user/register" method="post">
        <div class="form-group">
            <label>用户名:</label>
            <input type="text" name="username" class="form-control" required>
        </div>
        <div class="form-group">
            <label>密码:</label>
            <input type="password" name="password" class="form-control" required>
        </div>
        <div class="form-group">
            <label>手机号:</label>
            <input type="tel" name="phone" class="form-control">
        </div>
        <div class="form-group">
            <label>地址:</label>
            <input type="text" name="address" class="form-control">
        </div>
        <button type="submit" class="btn btn-primary">注册</button>
    </form>
</div>
</body>
</html>
③ 登录功能实现

流程:用户提交账号密码→验证用户名和密码→登录成功则创建 Session。

登录 Servlet(LoginServlet.java)

@WebServlet("/user/login")
public class LoginServlet extends HttpServlet {
    private UserService userService = new UserServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        // 调用Service验证
        User user = userService.login(username, password);
        if (user != null) {
            // 登录成功:创建Session,存储用户信息(只存必要字段)
            HttpSession session = req.getSession();
            session.setAttribute("loginUser", user);
            // 防Session固定攻击:重置Session ID
            session.invalidate();
            session = req.getSession(true);
            session.setAttribute("loginUser", user);
            // 跳转到首页
            resp.sendRedirect(req.getContextPath() + "/index.jsp");
        } else {
            req.setAttribute("error", "用户名或密码错误");
            req.getRequestDispatcher("/user/login.jsp").forward(req, resp);
        }
    }
}

UserService 登录验证逻辑

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    @Override
    public User login(String username, String password) {
        // 1. 根据用户名查询用户
        User user = userDao.findByUsername(username);
        if (user == null) {
            return null; // 用户名不存在
        }
        // 2. 验证密码(BCrypt比对)
        if (PasswordUtils.verify(password, user.getPassword())) {
            return user; // 验证通过
        }
        return null; // 密码错误
    }
}

3. 商品模块

① 商品列表(带分页)

流程:Servlet 接收分页参数(页码、每页条数)→调用 Service 查询商品列表和总条数→转发到 JSP 展示。

商品列表 Servlet(ProductListServlet.java)

@WebServlet("/product/list")
public class ProductListServlet extends HttpServlet {
    private ProductService productService = new ProductServiceImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取分页参数(默认第1页,每页10条)
        int pageNum = 1;
        int pageSize = 10;
        try {
            pageNum = Integer.parseInt(req.getParameter("pageNum"));
            pageSize = Integer.parseInt(req.getParameter("pageSize"));
        } catch (NumberFormatException e) {
            // 参数无效则用默认值
        }

        // 获取分类ID(可选,用于分类筛选)
        String categoryId = req.getParameter("categoryId");
        // 获取搜索关键词(可选)
        String keyword = req.getParameter("keyword");

        // 调用Service查询:分页数据和总条数
        PageBean<Product> pageBean = productService.findPage(pageNum, pageSize, categoryId, keyword);

        // 存入request域,转发到列表页
        req.setAttribute("pageBean", pageBean);
        req.setAttribute("categoryId", categoryId);
        req.setAttribute("keyword", keyword);
        req.getRequestDispatcher("/product/list.jsp").forward(req, resp);
    }
}

分页 Bean(PageBean.java)

public class PageBean<T> {
    private int pageNum; // 当前页码
    private int pageSize; // 每页条数
    private long totalCount; // 总记录数
    private int totalPage; // 总页数
    private List<T> list; // 当前页数据

    // 构造方法:计算总页数
    public PageBean(int pageNum, int pageSize, long totalCount, List<T> list) {
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.totalCount = totalCount;
        this.list = list;
        // 计算总页数(向上取整)
        this.totalPage = (int) (totalCount == 0 ? 1 : (totalCount + pageSize - 1) / pageSize);
    }

    // getter和setter
}

商品列表 JSP(list.jsp)

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %> <%-- 导入分页标签 --%>
<html>
<head>
    <title>商品列表</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <!-- 搜索框 -->
    <form action="${pageContext.request.contextPath}/product/list" method="get" class="form-inline">
        <input type="text" name="keyword" placeholder="搜索商品" class="form-control" 
               value="${not empty keyword ? keyword : ''}">
        <button type="submit" class="btn btn-default">搜索</button>
    </form>

    <!-- 商品列表 -->
    <div class="row">
        <c:forEach items="${pageBean.list}" var="p">
            <div class="col-md-3" style="margin-top: 20px;">
                <div class="thumbnail">
                    <img src="${pageContext.request.contextPath}/static/images/${p.image}" alt="${p.name}">
                    <div class="caption">
                        <h3>${p.name}</h3>
                        <p>价格:<fmt:formatNumber value="${p.price}" type="currency"/></p>
                        <p>库存:${p.stock > 0 ? p.stock + '件' : '无货'}</p>
                        <p>
                            <a href="${pageContext.request.contextPath}/product/detail?id=${p.id}" 
                               class="btn btn-info">查看详情</a>
                            <a href="${pageContext.request.contextPath}/cart/add?productId=${p.id}&quantity=1" 
                               class="btn btn-primary" 
                               <c:if test="${p.stock <= 0}">disabled</c:if>>加入购物车</a>
                        </p>
                    </div>
                </div>
            </div>
        </c:forEach>
    </div>

    <!-- 分页控件(使用自定义分页标签) -->
    <my:pagination 
        totalCount="${pageBean.totalCount}" 
        pageSize="${pageBean.pageSize}" 
        currentPage="${pageBean.pageNum}" 
        url="/product/list?categoryId=${categoryId}&keyword=${keyword}"
    />
</div>
</body>
</html>

4. 购物车模块

购物车数据存储在Session中(未登录用户)或数据库中(登录用户),这里实现登录用户的购物车(持久化)。

① 添加商品到购物车

流程:用户点击 “加入购物车”→Servlet 接收商品 ID 和数量→验证库存→调用 Service 添加(已存在则更新数量)。

添加购物车 Servlet(CartAddServlet.java)

@WebServlet("/cart/add")
public class CartAddServlet extends HttpServlet {
    private CartService cartService = new CartServiceImpl();
    private ProductService productService = new ProductServiceImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 验证登录
        User loginUser = (User) req.getSession().getAttribute("loginUser");
        if (loginUser == null) {
            // 未登录,重定向到登录页
            resp.sendRedirect(req.getContextPath() + "/user/login.jsp?msg=请先登录");
            return;
        }

        // 获取参数
        int productId = Integer.parseInt(req.getParameter("productId"));
        int quantity = Integer.parseInt(req.getParameter("quantity"));

        // 验证库存
        Product product = productService.findById(productId);
        if (product == null || product.getStock() < quantity) {
            resp.sendRedirect(req.getContextPath() + "/product/detail?id=" + productId + "&error=库存不足");
            return;
        }

        // 调用Service添加到购物车
        cartService.add(loginUser.getId(), productId, quantity);

        // 重定向到购物车页面
        resp.sendRedirect(req.getContextPath() + "/cart/cart.jsp");
    }
}

5. 订单模块

① 生成订单(从购物车结算)

流程:用户点击 “结算”→验证购物车商品库存→创建订单和订单项→扣减库存→清空购物车。

生成订单 Servlet(OrderCreateServlet.java)

@WebServlet("/order/create")
public class OrderCreateServlet extends HttpServlet {
    private OrderService orderService = new OrderServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 验证登录
        User loginUser = (User) req.getSession().getAttribute("loginUser");
        if (loginUser == null) {
            resp.sendRedirect(req.getContextPath() + "/user/login.jsp");
            return;
        }

        // 获取收货信息
        String address = req.getParameter("address");
        String phone = req.getParameter("phone");

        // 调用Service生成订单(事务管理)
        String orderId = orderService.createOrder(loginUser.getId(), address, phone);
        if (orderId != null) {
            // 生成成功,跳转到订单详情页
            resp.sendRedirect(req.getContextPath() + "/order/detail?id=" + orderId);
        } else {
            req.setAttribute("error", "创建订单失败,请重试");
            req.getRequestDispatcher("/cart/cart.jsp").forward(req, resp);
        }
    }
}

OrderService 核心逻辑(事务管理)

public class OrderServiceImpl implements OrderService {
    // 注意:所有DAO操作需使用同一个Connection,确保事务统一
    private Connection conn;

    @Override
    public String createOrder(int userId, String address, String phone) {
        try {
            // 1. 获取连接,关闭自动提交
            conn = DBHelper.getConnection();
            conn.setAutoCommit(false);

            // 2. 查询用户购物车
            CartDao cartDao = new CartDaoImpl(conn);
            List<Cart> cartList = cartDao.findByUserId(userId);
            if (cartList.isEmpty()) {
                return null; // 购物车为空
            }

            // 3. 验证库存并计算总金额
            ProductDao productDao = new ProductDaoImpl(conn);
            BigDecimal totalPrice = BigDecimal.ZERO;
            for (Cart cart : cartList) {
                Product product = productDao.findById(cart.getProductId());
                if (product.getStock() < cart.getQuantity()) {
                    throw new RuntimeException("商品《" + product.getName() + "》库存不足");
                }
                // 累加总金额
                totalPrice = totalPrice.add(
                    product.getPrice().multiply(new BigDecimal(cart.getQuantity()))
                );
            }

            // 4. 创建订单
            String orderId = "ORD" + System.currentTimeMillis() + RandomStringUtils.randomNumeric(4);
            Order order = new Order();
            order.setId(orderId);
            order.setUserId(userId);
            order.setTotalPrice(totalPrice);
            order.setStatus(0); // 待付款
            order.setAddress(address);
            order.setPhone(phone);
            order.setCreateTime(new Date());
            OrderDao orderDao = new OrderDaoImpl(conn);
            orderDao.insert(order);

            // 5. 创建订单项并扣减库存
            OrderItemDao itemDao = new OrderItemDaoImpl(conn);
            for (Cart cart : cartList) {
                Product product = productDao.findById(cart.getProductId());
                // 创建订单项
                OrderItem item = new OrderItem();
                item.setOrderId(orderId);
                item.setProductId(product.getId());
                item.setProductName(product.getName());
                item.setProductPrice(product.getPrice());
                item.setQuantity(cart.getQuantity());
                itemDao.insert(item);

                // 扣减库存
                product.setStock(product.getStock() - cart.getQuantity());
                productDao.updateStock(product);
            }

            // 6. 清空购物车
            cartDao.deleteByUserId(userId);

            // 7. 提交事务
            conn.commit();
            return orderId;

        } catch (Exception e) {
            // 回滚事务
            try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); }
            e.printStackTrace();
            return null;
        } finally {
            DBHelper.close(conn, null, null);
        }
    }
}

五、核心流程走通

1. 注册与登录

  1. 访问http://localhost:8080/shopping/user/register.jsp,填写注册信息提交;
  2. 注册成功后跳转到登录页,输入账号密码登录;
  3. 登录成功后,Session 中存储用户信息,跳转到首页。

2. 浏览与搜索商品

  1. 首页展示商品分类和热门商品;
  2. 点击分类或输入关键词搜索,进入商品列表页(带分页);
  3. 点击商品查看详情,可看到价格、库存、描述等信息。

3. 加入购物车与结算

  1. 在商品详情页点击 “加入购物车”,跳转至购物车页面;
  2. 购物车中可修改商品数量(需验证库存)或删除商品;
  3. 点击 “结算”,填写收货地址和电话,提交生成订单。

4. 订单管理

  1. 生成订单后,跳转到订单详情页,显示订单号、商品明细、总金额、状态(待付款);
  2. 访问 “我的订单” 列表,可查看所有订单及状态;
  3. 点击 “付款”“确认收货” 等按钮更新订单状态(简化版可手动修改状态演示)。

六、项目扩展与优化方向

本项目实现了核心功能,实际开发中还可扩展和优化:

  1. 功能扩展

    • 管理员模块(商品管理、订单管理、用户管理);
    • 支付集成(对接支付宝、微信支付);
    • 商品评价、收藏功能;
    • 优惠券、积分系统。
  2. 技术优化

    • 加入 Redis 缓存热门商品和分类数据,减轻数据库压力;
    • 异步处理订单通知(如短信提醒),使用消息队列;
    • 前端使用 Vue.js 重构,实现前后端分离,提升用户体验;
    • 加入日志系统(如 Logback),记录操作日志和异常。
  3. 安全增强

    • 实现图形验证码,防止注册和登录接口被恶意调用;
    • 敏感操作(如支付、修改密码)增加短信验证;
    • 定期备份数据库,防止数据丢失。

 最后小结:

通过这个在线购物系统的实战,你已经综合运用了 JSP、Servlet、JSTL、EL、过滤器、事务管理等核心技术,理解了 MVC 分层架构的实际应用。从需求分析到数据库设计,从模块实现到流程串联,每个环节都是对 Web 开发能力的锻炼。项目开发的过程,也是解决问题的过程:如何设计合理的表结构?如何处理事务确保数据一致性?如何防范安全漏洞?这些问题的解决经验,比代码本身更有价值。虽然 JSP 技术已逐渐被前后端分离架构取代,但其中蕴含的 Web 开发思想(如 MVC、请求处理流程、数据交互)是相通的。掌握这些思想,再学习 Spring Boot、Vue 等新技术时,会更容易理解其设计理念。未完待续..........

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐