1. 项目概述:JSP+Servlet网上商城系统核心架构解析
这个基于JavaWeb技术的电商系统,采用了经典的MVC分层架构。前端使用JSP实现动态页面渲染,Servlet作为控制器处理业务逻辑,MySQL作为数据存储层,配合AJAX实现异步交互。这种技术组合在中小型电商系统中非常典型,既能满足基础功能需求,又保持了良好的可维护性。
我在实际开发中发现,这种架构特别适合需要快速上线的中小型项目。相比Spring Boot等现代框架,JSP+Servlet的学习曲线更平缓,对新手开发者更友好。下面这张表格对比了主要技术组件的职责:
| 技术组件 | 在系统中的角色 | 典型应用场景 |
|---|---|---|
| JSP | 视图层 | 商品展示页、订单确认页 |
| Servlet | 控制层 | 用户登录验证、购物车操作 |
| MySQL | 数据持久层 | 商品信息存储、订单记录 |
| AJAX | 异步交互 | 购物车数量实时更新 |
提示:虽然现在流行前后端分离架构,但JSP+Servlet方案在传统企业信息化项目中仍有大量应用场景,特别是需要快速交付的内部管理系统。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境搭建与工具选型
2.1 基础开发环境配置
我推荐使用以下环境组合进行开发:
- JDK 1.8(长期支持版本,兼容性最佳)
- Tomcat 9.x(Servlet 4.0规范支持)
- MySQL 5.7(社区版即可)
- IntelliJ IDEA Ultimate(对JavaEE支持最好)
数据库连接池建议使用HikariCP,这是目前性能最好的连接池实现。在web.xml中需要配置:
xml复制<resource-ref>
<description>MySQL Datasource</description>
<res-ref-name>jdbc/ShopDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
2.2 项目目录结构规范
合理的项目结构能大幅提升开发效率。这是我经过多个项目验证的目录结构:
code复制/src
/main
/java # Java源代码
/com.shop.controller # Servlet类
/com.shop.dao # 数据访问层
/com.shop.model # 实体类
/com.shop.service # 业务逻辑层
/webapp
/WEB-INF
/lib # 依赖jar包
/views # JSP文件
/static # 静态资源
注意:JSP文件不要直接放在webapp根目录下,应该放在WEB-INF下以保证安全性,避免用户直接访问JSP文件。
3. 核心功能模块实现详解
3.1 用户认证模块开发
用户登录是电商系统的门户。我采用Session+Cookie的方式实现认证状态保持。关键Servlet代码如下:
java复制protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
UserService userService = new UserService();
User user = userService.login(username, password);
if(user != null) {
HttpSession session = request.getSession();
session.setAttribute("currentUser", user);
// 设置30分钟过期
session.setMaxInactiveInterval(30 * 60);
response.sendRedirect("index.jsp");
} else {
request.setAttribute("errorMsg", "用户名或密码错误");
request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
}
}
密码存储一定要加密!我推荐使用BCrypt算法:
java复制public static String hashPassword(String plainText) {
return BCrypt.hashpw(plainText, BCrypt.gensalt(12));
}
public static boolean checkPassword(String plainText, String hashed) {
return BCrypt.checkpw(plainText, hashed);
}
3.2 商品展示与分页查询
商品列表需要支持分页显示。我通常在DAO层实现分页逻辑:
java复制public List<Product> getProductsByPage(int pageNo, int pageSize) throws SQLException {
List<Product> products = new ArrayList<>();
String sql = "SELECT * FROM products LIMIT ? OFFSET ?";
try (Connection conn = DataSourceUtil.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, pageSize);
stmt.setInt(2, (pageNo - 1) * pageSize);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Product product = new Product();
product.setId(rs.getInt("id"));
// 其他字段映射...
products.add(product);
}
}
return products;
}
前端JSP页面使用JSTL标签库简化开发:
jsp复制<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:forEach items="${products}" var="product">
<div class="product-item">
<h3>${product.name}</h3>
<p>价格:¥${product.price}</p>
<button onclick="addToCart(${product.id})">加入购物车</button>
</div>
</c:forEach>
4. 购物车与订单系统实现
4.1 购物车功能设计
购物车是电商系统的核心功能。我采用Session临时存储+数据库持久化的混合方案:
java复制public class CartServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
Cart cart = (Cart) session.getAttribute("cart");
if (cart == null) {
cart = new Cart();
session.setAttribute("cart", cart);
}
String action = request.getParameter("action");
int productId = Integer.parseInt(request.getParameter("productId"));
if ("add".equals(action)) {
cart.addItem(productId, 1);
} else if ("remove".equals(action)) {
cart.removeItem(productId);
}
// AJAX响应
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.print("{\"totalItems\": " + cart.getTotalItems() + "}");
}
}
4.2 订单生成与支付流程
订单处理需要考虑事务完整性。我使用JDBC事务确保数据一致性:
java复制public boolean createOrder(Order order, List<OrderItem> items) {
Connection conn = null;
try {
conn = DataSourceUtil.getConnection();
conn.setAutoCommit(false); // 开启事务
// 1. 插入订单主表
orderDao.insertOrder(conn, order);
// 2. 插入订单明细
for (OrderItem item : items) {
orderItemDao.insertOrderItem(conn, item);
}
// 3. 扣减库存
for (OrderItem item : items) {
productDao.reduceStock(conn, item.getProductId(), item.getQuantity());
}
conn.commit();
return true;
} catch (SQLException e) {
if (conn != null) {
try { conn.rollback(); } catch (SQLException ex) {}
}
return false;
} finally {
if (conn != null) {
try { conn.close(); } catch (SQLException e) {}
}
}
}
5. 性能优化与安全防护
5.1 数据库查询优化
电商系统最常见的性能瓶颈在数据库。我总结了几个关键优化点:
- 索引优化:商品表必须在category_id、price等常用查询字段上建立索引
sql复制CREATE INDEX idx_category ON products(category_id);
CREATE INDEX idx_price ON products(price);
- 连接池配置:HikariCP的推荐配置
properties复制jdbcUrl=jdbc:mysql://localhost:3306/shop_db
username=root
password=123456
maximumPoolSize=20
minimumIdle=5
connectionTimeout=30000
idleTimeout=600000
maxLifetime=1800000
5.2 安全防护措施
电商系统必须重视安全性,我通常会实施以下防护:
- XSS防护:在JSP中使用JSTL的c:out标签自动转义
jsp复制<c:out value="${userInputContent}" />
- CSRF防护:为关键表单添加Token验证
jsp复制<input type="hidden" name="csrfToken" value="${sessionScope.csrfToken}" />
- SQL注入防护:必须使用PreparedStatement
java复制// 错误示范 - 有注入风险
String sql = "SELECT * FROM users WHERE username='" + username + "'";
// 正确做法
String sql = "SELECT * FROM users WHERE username=?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
6. 常见问题排查与调试技巧
6.1 JSP页面显示乱码问题
这是新手最常见的问题之一。解决方案是在每个JSP页面顶部添加:
jsp复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% request.setCharacterEncoding("UTF-8"); %>
同时在web.xml中配置全局过滤器:
xml复制<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6.2 AJAX请求返回乱码
Servlet响应AJAX请求时需要明确设置ContentType:
java复制response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("{\"status\":\"success\"}");
out.flush();
6.3 Tomcat热部署失效
在IDEA中确保配置正确:
- 打开Run/Debug Configurations
- 选择你的Tomcat配置
- 在Server标签页勾选"Update classes and resources"
- 设置"On frame deactivation"为"Update classes and resources"
7. 项目部署与上线准备
7.1 生产环境部署要点
- Tomcat优化配置(conf/server.xml):
xml复制<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="200"
minSpareThreads="10"
enableLookups="false"
acceptCount="100"
disableUploadTimeout="true"/>
- MySQL生产配置(my.cnf):
ini复制[mysqld]
innodb_buffer_pool_size=1G # 建议为物理内存的50-70%
innodb_log_file_size=256M
max_connections=200
query_cache_size=64M
7.2 日志记录方案
我建议使用Log4j2进行日志管理,配置示例:
xml复制<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="File" fileName="logs/shop.log"
filePattern="logs/shop-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
在实际项目中,我发现将JSP+Servlet与一些现代技术结合使用可以取得更好的效果。比如使用jQuery简化AJAX调用,或者引入Bootstrap改善前端界面。虽然这不是一个前沿技术栈,但对于需要快速开发、维护成本低的项目来说,这个架构仍然是非常实用的选择。
