1. 项目概述与核心架构设计
这个基于JavaWeb技术的网上商城系统采用了经典的MVC分层架构,使用JSP作为视图层、Servlet作为控制层、MySQL作为数据持久层。系统通过AJAX实现前后端异步交互,避免了传统表单提交导致的页面刷新问题。整套技术栈在电商类项目中具有典型代表性,涵盖了JavaWeb开发的核心技术要点。
从技术选型角度看,JSP+Servlet组合虽然不如Spring Boot等现代框架流行,但作为JavaEE的基础技术,理解其工作原理对于掌握Web开发本质至关重要。MySQL作为关系型数据库,提供了事务支持和ACID特性,非常适合电商场景中订单、库存等需要强一致性的业务模块。
提示:初学者常犯的错误是直接在JSP中编写业务逻辑代码,这会导致后期维护困难。正确的做法是严格遵循MVC分层,JSP仅负责展示,Servlet处理请求转发,业务逻辑放在JavaBean中。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境搭建与工具配置
2.1 基础环境准备
开发需要JDK 1.8或以上版本,推荐使用Tomcat 9作为Servlet容器。数据库方面,MySQL 5.7或8.0版本均可,需要注意字符集应设置为utf8mb4以支持完整的Unicode字符(特别是表情符号)。
IDE选择上,IntelliJ IDEA对JavaWeb项目支持最为完善。以下是关键配置步骤:
- 新建Dynamic Web Project
- 配置Tomcat运行时环境
- 添加MySQL JDBC驱动依赖
- 设置项目编码为UTF-8
2.2 Maven依赖管理
虽然项目可以使用传统lib目录方式引入jar包,但更推荐使用Maven管理依赖。核心依赖包括:
xml复制<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- Jackson for JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
</dependencies>
3. 数据库设计与实现
3.1 核心表结构设计
电商系统通常需要以下基础表:
- 用户表(users):存储用户基本信息
- 商品表(products):商品详情和库存
- 订单表(orders):订单主信息
- 订单明细表(order_items):订单商品明细
- 购物车表(cart):用户购物车数据
以下是用户表的DDL示例:
sql复制CREATE TABLE `users` (
`user_id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL COMMENT '存储加密后的密码',
`email` varchar(100) NOT NULL,
`phone` varchar(20) DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
3.2 数据库连接池配置
直接使用DriverManager获取连接效率低下,推荐使用连接池。以下是DBCP2配置示例:
java复制public class DBUtil {
private static BasicDataSource dataSource;
static {
dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/ecommerce?useSSL=false&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setInitialSize(5);
dataSource.setMaxTotal(20);
dataSource.setMaxIdle(10);
dataSource.setMinIdle(5);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
4. 核心功能模块实现
4.1 用户认证与会话管理
用户登录功能涉及密码加密和会话保持。以下是Servlet处理登录的核心代码:
java复制@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// 密码加密
String encryptedPwd = DigestUtils.md5DigestAsHex((password + "SALT").getBytes());
try (Connection conn = DBUtil.getConnection()) {
String sql = "SELECT * FROM users WHERE username=? AND password=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, encryptedPwd);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
// 登录成功,创建会话
HttpSession session = request.getSession();
session.setAttribute("user", rs.getString("username"));
session.setAttribute("userId", rs.getInt("user_id"));
// 返回JSON响应
response.setContentType("application/json");
response.getWriter().write("{\"success\":true}");
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户名或密码错误");
}
} catch (SQLException e) {
throw new ServletException("数据库错误", e);
}
}
}
4.2 商品展示与分页
商品列表需要支持分页展示,前端通过AJAX请求数据:
jsp复制<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div id="product-list">
<c:forEach items="${products}" var="product">
<div class="product-item">
<img src="${product.imageUrl}" alt="${product.name}">
<h3>${product.name}</h3>
<p class="price">¥${product.price}</p>
<button onclick="addToCart(${product.id})">加入购物车</button>
</div>
</c:forEach>
</div>
<div id="pagination">
<button onclick="loadProducts(${page-1})" ${page==1?'disabled':''}>上一页</button>
<span>${page}/${totalPages}</span>
<button onclick="loadProducts(${page+1})" ${page==totalPages?'disabled':''}>下一页</button>
</div>
<script>
function loadProducts(page) {
$.get('products?page=' + page, function(data) {
$('#product-list').html(data);
});
}
</script>
5. AJAX异步交互实现
5.1 购物车操作
购物车功能需要频繁的局部更新,非常适合使用AJAX:
javascript复制function addToCart(productId) {
$.ajax({
url: 'cart',
type: 'POST',
data: {action: 'add', productId: productId},
success: function(data) {
if(data.success) {
updateCartCount(data.totalItems);
showMessage('已添加到购物车');
} else {
showError(data.message);
}
},
error: function() {
showError('网络错误,请重试');
}
});
}
function updateCartCount(count) {
$('#cart-count').text(count);
}
对应的Servlet处理:
java复制@WebServlet("/cart")
public class CartServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
Integer userId = (Integer) session.getAttribute("userId");
if (userId == null) {
sendJsonResponse(response, false, "请先登录");
return;
}
String action = request.getParameter("action");
int productId = Integer.parseInt(request.getParameter("productId"));
try (Connection conn = DBUtil.getConnection()) {
if ("add".equals(action)) {
// 检查是否已存在
String checkSql = "SELECT quantity FROM cart WHERE user_id=? AND product_id=?";
PreparedStatement checkStmt = conn.prepareStatement(checkSql);
checkStmt.setInt(1, userId);
checkStmt.setInt(2, productId);
ResultSet rs = checkStmt.executeQuery();
if (rs.next()) {
// 更新数量
int newQty = rs.getInt("quantity") + 1;
String updateSql = "UPDATE cart SET quantity=? WHERE user_id=? AND product_id=?";
PreparedStatement updateStmt = conn.prepareStatement(updateSql);
updateStmt.setInt(1, newQty);
updateStmt.setInt(2, userId);
updateStmt.setInt(3, productId);
updateStmt.executeUpdate();
} else {
// 新增记录
String insertSql = "INSERT INTO cart(user_id, product_id, quantity) VALUES(?,?,1)";
PreparedStatement insertStmt = conn.prepareStatement(insertSql);
insertStmt.setInt(1, userId);
insertStmt.setInt(2, productId);
insertStmt.executeUpdate();
}
// 获取购物车总数量
String countSql = "SELECT SUM(quantity) AS total FROM cart WHERE user_id=?";
PreparedStatement countStmt = conn.prepareStatement(countSql);
countStmt.setInt(1, userId);
rs = countStmt.executeQuery();
int total = rs.next() ? rs.getInt("total") : 0;
sendJsonResponse(response, true, total);
}
} catch (SQLException e) {
throw new ServletException("数据库错误", e);
}
}
private void sendJsonResponse(HttpServletResponse response, boolean success, Object data)
throws IOException {
response.setContentType("application/json");
PrintWriter out = response.getWriter();
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> result = new HashMap<>();
result.put("success", success);
if (success) {
result.put("totalItems", data);
} else {
result.put("message", data);
}
out.print(mapper.writeValueAsString(result));
}
}
6. 系统安全与性能优化
6.1 安全防护措施
- SQL注入防护:始终使用PreparedStatement
- XSS防护:JSTL的<c:out>自动转义,或使用OWASP Java Encoder
- CSRF防护:为关键操作添加token验证
- 密码安全:加盐哈希存储,推荐BCrypt
- 会话固定防护:登录后重置sessionId
6.2 性能优化建议
-
数据库层面:
- 为常用查询字段添加索引
- 避免SELECT *,只查询需要的字段
- 合理使用连接查询与子查询
-
Servlet层面:
- 合理使用Filter减少重复代码
- 对静态资源使用默认Servlet
- 考虑使用@WebServlet的asyncSupported属性
-
JSP层面:
- 使用<%@ page isELIgnored="false" %>启用EL表达式
- 合理使用JSTL标签库减少脚本片段
- 考虑将静态内容静态化
7. 项目部署与运维
7.1 生产环境部署
- 推荐使用Tomcat 9 + MySQL 8组合
- 配置server.xml的Connector:
xml复制<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" maxThreads="200" minSpareThreads="25"/> - 配置context.xml启用资源缓存:
xml复制<Context cachingAllowed="true" cacheMaxSize="100000"/>
7.2 常见问题排查
-
中文乱码问题:
- 确保数据库、连接、JSP页面三处编码一致(UTF-8)
- 在Servlet中设置request.setCharacterEncoding("UTF-8")
- 在Filter中添加编码设置
-
AJAX请求返回乱码:
java复制response.setContentType("application/json;charset=UTF-8"); -
静态资源无法加载:
- 检查web.xml是否拦截了静态资源
- 确保路径正确,推荐使用<c:url>生成路径
8. 项目扩展与进阶方向
- 引入Redis缓存热门商品和购物车数据
- 使用Elasticsearch实现商品搜索
- 添加支付接口集成(支付宝、微信支付)
- 实现分布式会话管理
- 引入消息队列处理高并发订单
- 使用Docker容器化部署
对于希望深入JavaWeb开发的开发者,建议在掌握本系统后,逐步过渡到Spring框架体系。理解Servlet和JSP的工作原理将为学习Spring MVC打下坚实基础。
