1. 项目概述:嘟嘟蛋糕商城的技术架构与核心功能
这个基于JavaWeb的蛋糕商城系统采用了经典的JSP+Servlet技术栈,配合MySQL数据库实现了一个完整的B2C电商平台。作为一名有多年JavaWeb开发经验的工程师,我认为这种技术组合在中小型电商项目中依然具有显著优势——开发效率高、学习曲线平缓、运行稳定且资源消耗低。
系统主要包含以下核心模块:
- 用户端:商品展示、购物车、订单管理、支付对接(模拟)
- 管理端:商品CRUD、订单处理、数据统计
- 技术特色:采用JDBC直连数据库、Servlet处理业务逻辑、JSP渲染视图、AJAX实现异步交互、文件上传处理商品图片
提示:虽然现在流行Spring框架,但掌握原生Servlet/JSP开发能更好理解Web应用底层原理,这对排查复杂问题非常有帮助。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与项目搭建
2.1 开发环境准备
我推荐使用以下环境配置:
bash复制JDK 1.8+
Tomcat 9.x
MySQL 5.7+
Eclipse/IDEA
MySQL安装特别注意:
sql复制# 创建数据库时建议使用utf8mb4字符集
CREATE DATABASE dudu_cake DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
2.2 项目目录结构设计
标准的Maven项目结构如下:
code复制src/
├── main/
│ ├── java/ # Servlet和工具类
│ ├── resources/ # 配置文件
│ ├── webapp/
│ ├── WEB-INF/ # web.xml和lib
│ ├── css/ # 静态资源
│ ├── images/ # 上传的图片
├── test/ # 测试代码
2.3 数据库设计要点
蛋糕商城核心表结构设计示例:
sql复制CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT DEFAULT 0,
image_path VARCHAR(255),
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 订单表需要特别注意事务处理
CREATE TABLE orders (
id VARCHAR(32) PRIMARY KEY, # 使用UUID生成
user_id INT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status TINYINT DEFAULT 0, # 0-未支付 1-已支付...
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
3. 核心功能实现细节
3.1 用户认证与会话管理
采用Servlet Filter实现登录验证:
java复制@WebFilter("/*")
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String path = request.getRequestURI();
if(path.contains("/admin/") && request.getSession().getAttribute("admin") == null) {
((HttpServletResponse)res).sendRedirect("/login.jsp");
return;
}
chain.doFilter(req, res);
}
}
3.2 商品分页查询优化
JDBC分页的两种实现方式:
- MySQL LIMIT方式(推荐):
java复制String sql = "SELECT * FROM products LIMIT ?, ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, (pageNum-1)*pageSize);
stmt.setInt(2, pageSize);
- 内存分页(小数据量适用):
java复制List<Product> allProducts = productDao.getAll();
List<Product> pageProducts = allProducts.stream()
.skip((pageNum-1)*pageSize)
.limit(pageSize)
.collect(Collectors.toList());
3.3 AJAX购物车实现
前端jQuery代码示例:
javascript复制function addToCart(productId) {
$.ajax({
url: '/cart/add',
type: 'POST',
data: {productId: productId, quantity: 1},
success: function(data) {
$('#cart-count').text(data.totalItems);
}
});
}
对应Servlet处理:
java复制@WebServlet("/cart/add")
public class AddCartServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
HttpSession session = req.getSession();
Cart cart = (Cart)session.getAttribute("cart");
if(cart == null) {
cart = new Cart();
session.setAttribute("cart", cart);
}
// 处理添加逻辑...
resp.setContentType("application/json");
resp.getWriter().write("{\"totalItems\":"+cart.getTotalItems()+"}");
}
}
4. 文件上传与图片处理
4.1 使用commons-fileupload
配置web.xml:
xml复制<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.dudu.upload.UploadServlet</servlet-class>
<multipart-config>
<max-file-size>10485760</max-file-size> <!-- 10MB -->
</multipart-config>
</servlet>
上传处理代码:
java复制if(ServletFileUpload.isMultipartContent(request)) {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(request);
for(FileItem item : items) {
if(!item.isFormField()) {
String fileName = UUID.randomUUID() +
FilenameUtils.getExtension(item.getName());
File storeFile = new File(uploadPath, fileName);
item.write(storeFile);
// 保存到数据库...
}
}
}
4.2 图片处理建议
- 使用Thumbnailator生成缩略图:
java复制Thumbnails.of(new File("original.jpg"))
.size(200, 200)
.outputQuality(0.8)
.toFile(new File("thumbnail.jpg"));
- 前端图片懒加载:
html复制<img data-src="/images/product1.jpg" class="lazyload">
<script>
$(function() {
$("img.lazyload").lazyload({
effect: "fadeIn"
});
});
</script>
5. 性能优化与安全防护
5.1 数据库连接池配置
使用Tomcat JDBC Pool配置:
xml复制<Resource name="jdbc/DuduCakeDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/dudu_cake?useSSL=false"
username="root"
password="123456"
maxActive="100"
maxIdle="30"
maxWait="10000"/>
5.2 常见安全防护措施
- SQL注入防护:
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);
- XSS防护:
jsp复制<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
${fn:escapeXml(userInput)}
- CSRF防护:
jsp复制<input type="hidden" name="csrfToken" value="${sessionScope.csrfToken}">
6. 项目部署与运维
6.1 WAR包部署流程
- 使用Maven打包:
bash复制mvn clean package
- 部署到Tomcat:
bash复制cp target/dudu-cake.war $TOMCAT_HOME/webapps/
- 查看日志:
bash复制tail -f $TOMCAT_HOME/logs/catalina.out
6.2 数据库备份方案
MySQL定时备份脚本:
bash复制#!/bin/bash
DATE=$(date +%Y%m%d)
mysqldump -uroot -p123456 dudu_cake > /backup/dudu_cake_$DATE.sql
find /backup -type f -mtime +7 -exec rm {} \;
7. 开发中的典型问题与解决方案
7.1 中文乱码问题
统一编码配置:
- JSP页面头部:
jsp复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
- Servlet过滤器:
java复制request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
- MySQL连接字符串:
java复制jdbc:mysql://localhost:3306/dudu_cake?useUnicode=true&characterEncoding=UTF-8
7.2 事务管理示例
订单创建的事务处理:
java复制Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
// 1. 扣减库存
productDao.reduceStock(conn, productId, quantity);
// 2. 创建订单
orderDao.create(conn, order);
conn.commit();
} catch (SQLException e) {
if(conn != null) conn.rollback();
throw e;
} finally {
if(conn != null) conn.close();
}
7.3 性能监控技巧
使用Filter记录请求耗时:
java复制public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
long start = System.currentTimeMillis();
chain.doFilter(req, res);
long elapsed = System.currentTimeMillis() - start;
if(elapsed > 500) { // 记录慢请求
logger.warn("Slow request: " + ((HttpServletRequest)req).getRequestURI()
+ " took " + elapsed + "ms");
}
}
8. 项目扩展方向建议
- 加入Redis缓存:
java复制// 商品详情缓存示例
String cacheKey = "product:" + productId;
String json = redis.get(cacheKey);
if(json != null) {
return objectMapper.readValue(json, Product.class);
} else {
Product product = productDao.getById(productId);
redis.setex(cacheKey, 3600, objectMapper.writeValueAsString(product));
return product;
}
- 改用MyBatis简化JDBC:
xml复制<!-- mapper示例 -->
<select id="getProductsByPage" resultType="Product">
SELECT * FROM products
LIMIT #{offset}, #{pageSize}
</select>
- 增加Elasticsearch商品搜索:
java复制SearchResponse response = client.prepareSearch("products")
.setQuery(QueryBuilders.multiMatchQuery(keywords, "name", "description"))
.setFrom(offset).setSize(pageSize)
.get();
这个项目虽然采用了传统技术栈,但涵盖了Web开发的各个核心环节。我在实际开发中发现,处理好文件上传路径的安全检查和数据库事务的隔离级别是两个最容易出问题的点。建议在正式环境中一定要对上传文件做病毒扫描,并且使用READ_COMMITTED以上的事务隔离级别来处理订单业务。
