【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(一)
作者:笙囧同学 🧑💻
技术栈: Java + JSP + Servlet + MySQL + Bootstrap
项目难度: ⭐⭐⭐⭐⭐
📖 前言
大家好,我是笙囧同学!👋
最近花了一个多月的时间,从零开始设计并实现了一个企业级的网上购书网站系统。这个项目不仅仅是一个简单的CRUD应用,而是一个功能完整、安全可靠、用户体验优秀的电商平台。
在这篇文章中,我将详细分享整个项目的设计思路、技术选型、核心功能实现以及踩过的坑。希望能给正在学习Java Web开发的小伙伴们一些启发和帮助!
💡 温馨提示:文章较长,建议收藏后慢慢阅读。文末会提供完整的源码下载链接!
🎯 项目概览
📊 项目基本信息
项目属性 | 详细信息 |
---|---|
项目名称 | 网上购书网站系统 |
项目类型 | B2C电子商务平台 |
开发周期 | 30天 |
代码量 | 8000+ 行 |
功能模块 | 5大核心模块 |
技术栈 | Java Web全栈 |
🏗️ 系统架构总览
🎨 项目亮点
- 🔐 企业级安全防护:多层安全验证,防SQL注入、XSS攻击
- 📱 响应式设计:完美适配PC端和移动端
- 🚀 高性能架构:连接池、缓存优化、分页查询
- 🛠️ 完善的测试体系:单元测试、集成测试、性能测试
- 📚 详细的文档:从需求分析到部署运维的完整文档
🎓 核心技术知识点深度解析
在开始介绍技术选型之前,让我先为大家梳理一下这个项目涉及的核心技术知识点,这些都是Java Web开发的精髓所在!作为一名深耕Java Web开发多年的程序员,我将从理论到实践,为大家详细讲解每个技术点的原理、应用场景和最佳实践。
🔍 Java Web技术原理深度剖析
📚 Servlet技术核心原理
Servlet是什么?
Servlet是运行在Web服务器上的Java程序,它是Java Web开发的基石。让我们深入了解Servlet的工作原理:
Servlet核心概念详解:
-
Servlet容器(Container)
- 负责管理Servlet的生命周期
- 提供网络服务,解析请求并构造响应
- 管理Servlet实例的创建、初始化、服务和销毁
-
ServletConfig对象
- 包含Servlet的配置信息
- 在init()方法中传入
- 可以获取初始化参数和ServletContext
-
ServletContext对象
- 代表整个Web应用程序
- 所有Servlet共享同一个ServletContext
- 可以用来存储应用级别的数据
实际代码示例:
public class BookServlet extends HttpServlet {private BookDAO bookDAO;@Overridepublic void init() throws ServletException {// 初始化阶段,只执行一次super.init();bookDAO = new BookDAO();// 获取初始化参数String dbUrl = getInitParameter("dbUrl");String dbUser = getInitParameter("dbUser");// 初始化数据库连接bookDAO.initConnection(dbUrl, dbUser);System.out.println("BookServlet初始化完成");}@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 每次请求都会调用String method = request.getMethod();// 根据HTTP方法分发请求if ("GET".equals(method)) {doGet(request, response);} else if ("POST".equals(method)) {doPost(request, response);} else if ("PUT".equals(method)) {doPut(request, response);} else if ("DELETE".equals(method)) {doDelete(request, response);}}@Overridepublic void destroy() {// 销毁阶段,释放资源if (bookDAO != null) {bookDAO.closeConnection();}System.out.println("BookServlet销毁完成");}
}
🌐 HTTP协议深度解析
HTTP请求响应模型:
HTTP状态码深度理解:
状态码类别 | 含义 | 常用状态码 | 应用场景 |
---|---|---|---|
1xx 信息性 | 请求已接收,继续处理 | 100 Continue | 大文件上传 |
2xx 成功 | 请求已成功处理 | 200 OK, 201 Created | 正常响应 |
3xx 重定向 | 需要进一步操作 | 301 Moved, 302 Found | 页面跳转 |
4xx 客户端错误 | 请求有语法错误 | 400 Bad Request, 404 Not Found | 参数错误 |
5xx 服务器错误 | 服务器处理错误 | 500 Internal Error, 503 Unavailable | 系统异常 |
🗄️ JDBC技术深度讲解
JDBC架构原理:
JDBC核心接口详解:
-
DriverManager类
- 管理数据库驱动程序
- 建立数据库连接
- 选择合适的驱动程序
-
Connection接口
- 代表与数据库的连接
- 管理事务
- 创建Statement对象
-
Statement接口
- 执行SQL语句
- 三种类型:Statement、PreparedStatement、CallableStatement
-
ResultSet接口
- 表示查询结果集
- 提供游标操作
- 支持不同的滚动类型
JDBC最佳实践代码:
public class BookDAO {private static final String DB_URL = "jdbc:mysql://localhost:3306/bookstore";private static final String DB_USER = "root";private static final String DB_PASSWORD = "password";// 使用连接池private DataSource dataSource;public BookDAO() {// 初始化连接池HikariConfig config = new HikariConfig();config.setJdbcUrl(DB_URL);config.setUsername(DB_USER);config.setPassword(DB_PASSWORD);config.setMaximumPoolSize(20);config.setMinimumIdle(5);config.setConnectionTimeout(30000);config.setIdleTimeout(600000);config.setMaxLifetime(1800000);this.dataSource = new HikariDataSource(config);}public List<Book> getBooksByCategory(String category, int page, int size) {List<Book> books = new ArrayList<>();// 使用PreparedStatement防止SQL注入String sql = "SELECT book_id, title, author, price, stock " +"FROM books WHERE category = ? " +"ORDER BY created_at DESC " +"LIMIT ? OFFSET ?";try (Connection conn = dataSource.getConnection();PreparedStatement pstmt = conn.prepareStatement(sql)) {// 设置参数pstmt.setString(1, category);pstmt.setInt(2, size);pstmt.setInt(3, page * size);try (ResultSet rs = pstmt.executeQuery()) {while (rs.next()) {Book book = new Book();book.setBookId(rs.getInt("book_id"));book.setTitle(rs.getString("title"));book.setAuthor(rs.getString("author"));book.setPrice(rs.getBigDecimal("price"));book.setStock(rs.getInt("stock"));books.add(book);}}} catch (SQLException e) {// 记录详细的错误信息logger.error("查询图书失败: category={}, page={}, size={}",category, page, size, e);throw new DAOException("查询图书失败", e);}return books;}// 事务处理示例public boolean transferStock(int fromBookId, int toBookId, int quantity) {Connection conn = null;try {conn = dataSource.getConnection();conn.setAutoCommit(false); // 开启事务// 减少源图书库存String sql1 = "UPDATE books SET stock = stock - ? WHERE book_id = ? AND stock >= ?";try (PreparedStatement pstmt1 = conn.prepareStatement(sql1)) {pstmt1.setInt(1, quantity);pstmt1.setInt(2, fromBookId);pstmt1.setInt(3, quantity);int affected1 = pstmt1.executeUpdate();if (affected1 == 0) {throw new BusinessException("源图书库存不足");}}// 增加目标图书库存String sql2 = "UPDATE books SET stock = stock + ? WHERE book_id = ?";try (PreparedStatement pstmt2 = conn.prepareStatement(sql2)) {pstmt2.setInt(1, quantity);pstmt2.setInt(2, toBookId);pstmt2.executeUpdate();}conn.commit(); // 提交事务return true;} catch (Exception e) {if (conn != null) {try {conn.rollback(); // 回滚事务} catch (SQLException ex) {logger.error("事务回滚失败", ex);}}logger.error("库存转移失败", e);return false;} finally {if (conn != null) {try {conn.setAutoCommit(true); // 恢复自动提交conn.close();} catch (SQLException e) {logger.error("关闭连接失败", e);}}}}
}
🎨 JSP技术深度解析
JSP(JavaServer Pages)工作原理:
JSP本质上是Servlet的一种简化形式,让我们深入了解JSP的编译和执行过程:
flowchart TDA[JSP页面] --> B[JSP引擎]B --> C{首次访问?}C -->|是| D[JSP编译阶段]C -->|否| H[直接执行Servlet]D --> E[解析JSP语法]E --> F[生成Java源码]F --> G[编译成Servlet字节码]G --> H[执行Servlet]H --> I[生成HTML响应]I --> J[返回给客户端]subgraph "JSP编译详细过程"D1[解析指令标签 <%@ %>]D2[解析脚本片段 <% %>]D3[解析表达式 <%= %>]D4[解析声明 <%! %>]D5[解析动作标签 <jsp:>]D6[解析EL表达式 ${}]endE --> D1 --> D2 --> D3 --> D4 --> D5 --> D6style A fill:#e3f2fdstyle J fill:#c8e6c9
JSP核心语法详解:
- 指令标签(Directive)
<%-- 页面指令:设置页面属性 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8" import="java.util.*"errorPage="error.jsp" isErrorPage="false" %><%-- 包含指令:静态包含其他文件 --%>
<%@ include file="header.jsp" %><%-- 标签库指令:引入标签库 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 脚本元素详解
<%-- 声明:定义变量和方法 --%>
<%!private int visitCount = 0;public String formatPrice(double price) {return String.format("¥%.2f", price);}
%><%-- 脚本片段:Java代码块 --%>
<%visitCount++;List<Book> books = (List<Book>) request.getAttribute("books");String currentUser = (String) session.getAttribute("username");
%><%-- 表达式:输出值 --%>
<p>访问次数:<%= visitCount %></p>
<p>当前用户:<%= currentUser != null ? currentUser : "游客" %></p>
- EL表达式深度应用
<%-- 基本语法 --%>
<p>用户名:${sessionScope.username}</p>
<p>图书数量:${requestScope.books.size()}</p><%-- 运算符使用 --%>
<p>总价:${book.price * book.quantity}</p>
<p>是否有库存:${book.stock > 0 ? '有货' : '缺货'}</p><%-- 集合操作 --%>
<c:forEach items="${books}" var="book" varStatus="status"><tr class="${status.index % 2 == 0 ? 'even' : 'odd'}"><td>${book.title}</td><td>${book.author}</td><td>${book.price}</td></tr>
</c:forEach>
🔧 Maven构建工具深度讲解
Maven核心概念:
Maven生命周期详解:
阶段 | 描述 | 主要任务 |
---|---|---|
validate | 验证项目 | 检查项目结构和必要信息 |
compile | 编译源码 | 编译src/main/java下的源码 |
test | 运行测试 | 执行src/test/java下的测试 |
package | 打包项目 | 创建JAR/WAR文件 |
verify | 验证包 | 运行集成测试验证包的有效性 |
install | 安装到本地 | 将包安装到本地仓库 |
deploy | 部署到远程 | 将包部署到远程仓库 |
实际项目的pom.xml配置详解:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!-- 项目基本信息 --><groupId>com.shengjiongtongxue</groupId><artifactId>bookstore-web</artifactId><version>1.0.0</version><packaging>war</packaging><name>网上购书网站</name><description>基于Java Web的企业级购书平台</description><!-- 属性配置 --><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- 依赖版本管理 --><servlet.version>4.0.1</servlet.version><jsp.version>2.3.3</jsp.version><jstl.version>1.2</jstl.version><mysql.version>8.0.33</mysql.version><junit.version>5.9.2</junit.version><mockito.version>5.1.1</mockito.version></properties><!-- 依赖管理 --><dependencies><!-- Servlet API --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>${servlet.version}</version><scope>provided</scope></dependency><!-- JSP API --><dependency><groupId>javax.servlet.jsp</groupId><artifactId>javax.servlet.jsp-api</artifactId><version>${jsp.version}</version><scope>provided</scope></dependency><!-- JSTL --><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>${jstl.version}</version></dependency><!-- MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!-- 连接池 --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.0.1</version></dependency><!-- JSON处理 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency><!-- 日志框架 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.7</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.4.8</version></dependency><!-- 测试依赖 --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>${junit.version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>${mockito.version}</version><scope>test</scope></dependency></dependencies><!-- 构建配置 --><build><finalName>bookstore</finalName><plugins><!-- 编译插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.11.0</version><configuration><source>11</source><target>11</target><encoding>UTF-8</encoding></configuration></plugin><!-- WAR打包插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>3.3.1</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin><!-- 测试插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.1.2</version><configuration><includes><include>**/*Test.java</include><include>**/*Tests.java</include></includes></configuration></plugin><!-- Jetty插件 - 用于开发测试 --><plugin><groupId>org.eclipse.jetty</groupId><artifactId>jetty-maven-plugin</artifactId><version>11.0.15</version><configuration><httpConnector><port>8080</port></httpConnector><webApp><contextPath>/bookstore</contextPath></webApp><reload>manual</reload></configuration></plugin></plugins></build><!-- 配置文件 --><profiles><!-- 开发环境 --><profile><id>dev</id><activation><activeByDefault>true</activeByDefault></activation><properties><db.url>jdbc:mysql://localhost:3306/bookstore_dev</db.url><db.username>root</db.username><db.password>password</db.password></properties></profile><!-- 测试环境 --><profile><id>test</id><properties><db.url>jdbc:mysql://test-server:3306/bookstore_test</db.url><db.username>test_user</db.username><db.password>test_password</db.password></properties></profile><!-- 生产环境 --><profile><id>prod</id><properties><db.url>jdbc:mysql://prod-server:3306/bookstore_prod</db.url><db.username>prod_user</db.username><db.password>prod_password</db.password></properties></profile></profiles>
</project>
📚 Java Web核心技术栈
mindmaproot((Java Web技术栈))前端技术HTML5语义化标签表单验证本地存储响应式设计CSS3Flexbox布局Grid布局动画效果媒体查询JavaScriptDOM操作事件处理Ajax异步ES6语法Bootstrap栅格系统组件库响应式工具主题定制后端技术Java基础面向对象集合框架异常处理多线程Servlet生命周期请求处理会话管理过滤器JSP指令标签动作标签EL表达式JSTL标签库JDBC连接管理预编译语句事务控制连接池数据库技术MySQL存储引擎索引优化查询优化事务机制SQL语言DDL语句DML语句DCL语句存储过程开发工具Maven依赖管理生命周期插件机制多模块项目Git版本控制分支管理协作开发代码回滚
🏗️ MVC架构模式深度剖析
🎯 设计模式在Java Web中的应用
在企业级Java Web开发中,设计模式的合理运用能够大大提高代码的可维护性和扩展性。让我详细讲解几个核心设计模式:
1. MVC模式(Model-View-Controller)
实际代码实现:
// Model层 - 实体类
public class Book {private int bookId;private String title;private String author;private BigDecimal price;private int stock;// 构造方法、getter、setter省略// 业务方法public boolean isAvailable() {return stock > 0;}public BigDecimal calculateDiscountPrice(double discountRate) {return price.multiply(BigDecimal.valueOf(1 - discountRate));}
}// Model层 - 业务逻辑
public class BookService {private BookDAO bookDAO;public BookService() {this.bookDAO = new BookDAO();}public List<Book> searchBooks(String keyword, String category, int page, int size) {// 参数验证if (keyword == null || keyword.trim().isEmpty()) {throw new IllegalArgumentException("搜索关键词不能为空");}// 业务逻辑处理List<Book> books = bookDAO.searchBooks(keyword, category, page, size);// 数据后处理return books.stream().filter(Book::isAvailable).collect(Collectors.toList());}
}// Controller层 - 控制器
@WebServlet("/books")
public class BookController extends HttpServlet {private BookService bookService;@Overridepublic void init() throws ServletException {this.bookService = new BookService();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String action = request.getParameter("action");try {switch (action) {case "search":handleSearch(request, response);break;case "detail":handleDetail(request, response);break;default:handleList(request, response);}} catch (Exception e) {handleError(request, response, e);}}private void handleSearch(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String keyword = request.getParameter("keyword");String category = request.getParameter("category");int page = Integer.parseInt(request.getParameter("page"));int size = Integer.parseInt(request.getParameter("size"));List<Book> books = bookService.searchBooks(keyword, category, page, size);request.setAttribute("books", books);request.setAttribute("keyword", keyword);request.setAttribute("category", category);RequestDispatcher dispatcher = request.getRequestDispatcher("/books.jsp");dispatcher.forward(request, response);}
}
2. DAO模式(Data Access Object)
DAO模式实现:
// DAO接口定义
public interface BookDAO {// 基本CRUD操作void save(Book book);void update(Book book);void delete(int bookId);Book findById(int bookId);List<Book> findAll();// 业务查询方法List<Book> findByCategory(String category);List<Book> searchByKeyword(String keyword);List<Book> findPopularBooks(int limit);// 分页查询List<Book> findWithPagination(int page, int size);int getTotalCount();
}// DAO接口实现
public class BookDAOImpl implements BookDAO {private DataSource dataSource;public BookDAOImpl() {this.dataSource = DataSourceFactory.getDataSource();}@Overridepublic void save(Book book) {String sql = "INSERT INTO books (title, author, publisher, price, stock, category) " +"VALUES (?, ?, ?, ?, ?, ?)";try (Connection conn = dataSource.getConnection();PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {pstmt.setString(1, book.getTitle());pstmt.setString(2, book.getAuthor());pstmt.setString(3, book.getPublisher());pstmt.setBigDecimal(4, book.getPrice());pstmt.setInt(5, book.getStock());pstmt.setString(6, book.getCategory());int affectedRows = pstmt.executeUpdate();if (affectedRows == 0) {throw new DAOException("保存图书失败,没有行被影响");}// 获取生成的主键try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {if (generatedKeys.next()) {book.setBookId(generatedKeys.getInt(1));}}} catch (SQLException e) {throw new DAOException("保存图书失败", e);}}@Overridepublic List<Book> searchByKeyword(String keyword) {String sql = "SELECT * FROM books WHERE " +"(title LIKE ? OR author LIKE ? OR description LIKE ?) " +"AND stock > 0 ORDER BY created_at DESC";List<Book> books = new ArrayList<>();String searchPattern = "%" + keyword + "%";try (Connection conn = dataSource.getConnection();PreparedStatement pstmt = conn.prepareStatement(sql)) {pstmt.setString(1, searchPattern);pstmt.setString(2, searchPattern);pstmt.setString(3, searchPattern);try (ResultSet rs = pstmt.executeQuery()) {while (rs.next()) {books.add(mapResultSetToBook(rs));}}} catch (SQLException e) {throw new DAOException("搜索图书失败", e);}return books;}// 结果集映射方法private Book mapResultSetToBook(ResultSet rs) throws SQLException {Book book = new Book();book.setBookId(rs.getInt("book_id"));book.setTitle(rs.getString("title"));book.setAuthor(rs.getString("author"));book.setPublisher(rs.getString("publisher"));book.setPrice(rs.getBigDecimal("price"));book.setStock(rs.getInt("stock"));book.setCategory(rs.getString("category"));book.setCreatedAt(rs.getTimestamp("created_at"));return book;}
}
3. 工厂模式(Factory Pattern)
// 数据源工厂
public class DataSourceFactory {private static DataSource dataSource;private static final Object lock = new Object();public static DataSource getDataSource() {if (dataSource == null) {synchronized (lock) {if (dataSource == null) {dataSource = createDataSource();}}}return dataSource;}private static DataSource createDataSource() {HikariConfig config = new HikariConfig();config.setJdbcUrl(ConfigManager.getProperty("db.url"));config.setUsername(ConfigManager.getProperty("db.username"));config.setPassword(ConfigManager.getProperty("db.password"));config.setMaximumPoolSize(20);config.setMinimumIdle(5);return new HikariDataSource(config);}
}// DAO工厂
public class DAOFactory {public static BookDAO getBookDAO() {return new BookDAOImpl();}public static UserDAO getUserDAO() {return new UserDAOImpl();}public static OrderDAO getOrderDAO() {return new OrderDAOImpl();}
}
🔄 HTTP请求处理生命周期
下篇的链接为:https://editor.csdn.net/md/?articleId=149720198