固原市网站建设_网站建设公司_小程序网站_seo优化
2025/12/26 16:48:55 网站建设 项目流程

JavaWeb实现图书管理系统

在开发一个典型的JavaWeb应用时,图书管理系统是一个经典的教学与实践项目。它涵盖了从数据库设计、DAO层封装、业务逻辑处理到前端交互的完整流程。本文将带你一步步构建一个功能完整的图书管理系统,重点解决实际开发中常见的痛点,比如SQL注入风险、分页查询效率、验证码安全机制等。

系统采用经典的三层架构:表现层(JSP + Servlet)、业务逻辑层(Service)、数据访问层(DAO),并借助C3P0连接池和Apache DbUtils简化数据库操作。整个项目结构清晰,适合初学者理解MVC模式,也具备一定的扩展性。


数据库设计:简洁而实用

系统的数据存储基于MySQL,核心表包括booksusers,分别用于管理图书信息和用户登录凭证。

图书表(books)

CREATE TABLE books ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, zuoze VARCHAR(50), price DOUBLE, riqi DATE );

这里zuoze是“作者”的拼音字段名,虽然语义不够规范,但在教学场景下可以接受。建议在生产环境中使用英文命名如author,避免后续国际化问题。日期字段使用DATE类型,前端通过yyyy-MM-dd格式输入即可自动转换。

用户表(users)

CREATE TABLE users ( yhname VARCHAR(50) PRIMARY KEY, pwd VARCHAR(50) );

用户名作为主键,密码明文存储——这显然不适合真实系统。在实际项目中应使用加密算法(如BCrypt)对密码哈希处理,并引入角色权限控制。但就学习目的而言,这种简化有助于聚焦核心流程。

提示:中文字段名和明文密码仅用于演示,请勿照搬至生产环境。


数据访问层(DAO):灵活封装查询逻辑

DAO层是系统与数据库交互的核心。我们使用QueryRunner配合ResultSetHandler来完成CRUD操作,既避免了手动管理Connection,又减少了样板代码。

BookDao 中的通用映射方法

public Book mapToBook(Map<String, Object> map) { Class<?> c = Book.class; Book book = new Book(); for (Map.Entry<String, Object> entry : map.entrySet()) { String fieldName = entry.getKey(); Object fieldValue = entry.getValue(); try { Field field = c.getDeclaredField(fieldName); field.setAccessible(true); field.set(book, fieldValue); } catch (NoSuchFieldException | IllegalAccessException e) { // 忽略不存在或无法设置的字段 } } return book; }

这个方法利用反射将Map<String, Object>映射为Book实体,比硬编码 setter 更具通用性。不过要注意字段类型匹配问题,例如数据库返回的java.sql.Date与实体类中的java.util.Date兼容,但如果遇到 BigDecimal 转 double 等情况可能抛异常。

分页与条件查询的安全隐患

原代码中的分页查询拼接了部分SQL字符串:

String sql1 = "SELECT * FROM books " + "WHERE (name LIKE '%" + key + "%' OR zuoze LIKE '%" + key + "%') " + "AND zuoze LIKE '%" + zuoze + "%' " + "AND price >= ? AND price <= ? LIMIT ?,?";

这种方式存在明显的SQL注入风险。虽然keyzuoze没有用参数化,但LIKE子句仍可通过特殊字符构造恶意请求。更安全的做法是全部使用占位符:

String sql = "SELECT * FROM books WHERE " + "(name LIKE ? OR zuoze LIKE ?) AND zuoze LIKE ? AND price BETWEEN ? AND ? LIMIT ?, ?"; List<Object> params = Arrays.asList( "%" + key + "%", "%" + key + "%", "%" + zuoze + "%", minPrice, maxPrice, pageSize * (pageNo - 1), pageSize );

然后调用qr.query(sql, rsh, params.toArray()),彻底杜绝注入可能。


服务层(Service):轻量级协调者

Service 层主要负责事务协调和异常处理。当前实现较为简单,每个方法都独立捕获 SQLException 并打印堆栈,虽便于调试,但不利于上层统一处理错误状态。

public void insertOrUpdate(Book book) { try { if (book.getId() != 0) { new BookDao().update(book); } else { new BookDao().insert(book); } } catch (SQLException e) { e.printStackTrace(); } }

更好的做法是向上抛出受检异常或封装为自定义异常(如BookServiceException),由控制器决定如何响应。此外,若未来需要支持事务一致性(例如同时更新多张表),应在 Service 层统一获取 Connection 并传递给 DAO。


控制器(Servlet):基于反射的路由机制

BookServlet使用了简单的反射机制来实现方法级路由:

String uri = request.getRequestURI(); String methodName = uri.substring(uri.lastIndexOf("/") + 1); Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this, request, response);

这是一种轻量级的“伪框架”设计,省去了多个if-else判断。优点是新增功能只需添加新方法,URL路径自动映射;缺点是缺乏参数校验、权限拦截等高级特性,且容易暴露内部方法名。

⚠️ 安全提醒:如果未加限制,攻击者可通过枚举方法名尝试调用私有方法。建议增加白名单机制或使用注解标记可访问方法。

另一个值得关注的点是验证码验证逻辑:

HttpSession session = request.getSession(); String token = (String) session.getAttribute("code"); String vCode = request.getParameter("vCode"); if (!vCode.equalsIgnoreCase(token)) { response.sendRedirect("/info.jsp"); return; } session.removeAttribute("code"); // 一次性使用

这是典型的会话绑定验证码模式,有效防止机器人批量提交。注意验证码区分大小写与否应根据生成规则一致,且应在验证后立即清除,防止重放攻击。


工具类设计:连接池与工具方法

JdbcUtil:C3P0连接池配置

private static final ComboPooledDataSource ds = new ComboPooledDataSource(); public static final QueryRunner qr = new QueryRunner(ds); static { try { ds.setJdbcUrl("jdbc:mysql://localhost:3306/world?useSSL=false&serverTimezone=UTC"); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setUser("root"); ds.setPassword("293016wy"); // 连接池参数 ds.setInitialPoolSize(5); ds.setMinPoolSize(5); ds.setMaxPoolSize(20); } catch (Exception e) { e.printStackTrace(); } }

C3P0 提供了稳定的连接复用能力,避免频繁创建销毁连接带来的性能损耗。这里的配置适用于小规模并发,高负载环境下可适当提升最大连接数并启用测试查询(testConnectionOnCheckout)确保连接有效性。

ValidateCode:图形验证码生成

验证码图片生成使用 AWT 绘图,包含干扰线和随机字符:

g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255))); g.drawLine(xs, ys, xe, ye);

这类验证码对普通爬虫有一定防御作用,但面对OCR识别仍有局限。生产环境推荐接入滑动验证码或短信验证等更强的身份核验方式。


前端页面:JSP + JavaScript 实现动态交互

主页跳转与初始化加载

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:forward page="/BookServlet/getAll?key=&zuoze=&minPrice=&maxPrice=&pageNo=1&pageSize=5"/>

通过<jsp:forward>实现首次请求的数据预加载,避免首页空白。这种方式比客户端重定向更快,用户体验更好。

all.jsp:表格展示与搜索联动

<script> function search(toPageNo) { let key = document.getElementById("key").value; let zuoze = document.getElementById("zuoze").value; let minPrice = document.getElementById("minPrice").value; let maxPrice = document.getElementById("maxPrice").value; location.href = "BookServlet/getAll?key="+key+"&zuoze="+zuoze+ "&minPrice="+minPrice+"&maxPrice="+maxPrice+"&pageNo="+toPageNo+"&pageSize=5"; } </script>

JavaScript 函数search()构造带分页参数的URL,实现无刷新条件查询。可以进一步优化为AJAX请求,配合JSON返回结果局部刷新表格,减少页面闪烁。

m.jsp:新增/修改共用表单

<h1>${empty book ? '新增' : '修改'} 图书</h1> <form action="BookServlet/submit"> <c:if test="${not empty book}"> <input type="hidden" name="id" value="${book.id}"/> </c:if> <!-- 其他字段 --> </form>

通过 EL 表达式判断book是否为空,动态切换标题和隐藏ID字段,实现了“一表两用”。这是JSP开发中常见的技巧,减少了重复页面的维护成本。


登录与会话管理:基础安全机制

DengServlet 处理用户登录:

User u = new UserService().user(username, pwd); if (u != null) { HttpSession session = request.getSession(); session.setAttribute("user", u); session.setMaxInactiveInterval(1800); // 30分钟超时 Cookie cookie = new Cookie("JSESSIONID", session.getId()); cookie.setPath(request.getContextPath()); cookie.setMaxAge(1800); response.addCookie(cookie); response.sendRedirect("/index.jsp"); } else { response.sendRedirect("sb.jsp"); }

这里手动设置了 Cookie 的 JSESSIONID,其实并不必要——容器默认已自动发送该Cookie。显式设置反而可能导致双会话ID问题。正确的做法是依赖容器管理会话,仅在跨域或分布式部署时才考虑自定义Token机制。


总结与建议

这套图书管理系统完整展示了JavaWeb开发的核心技术栈:

  • 连接池管理:C3P0 提升数据库访问性能;
  • ORM简化:DbUtils 减少JDBC模板代码;
  • MVC分离:Servlet 控制流程,JSP 渲染视图;
  • 安全性基础:验证码防刷、会话认证;
  • 前端交互:JS驱动分页与搜索。

尽管如此,仍有几点值得改进:

  1. 密码不应明文存储,应使用 BCrypt 或 SHA-256 加盐哈希;
  2. SQL注入防护不足,所有动态条件应使用参数化查询;
  3. 缺少全局异常处理,建议引入 Filter 或基类统一捕获;
  4. 前端技术陈旧,可结合 Bootstrap 或 Vue.js 提升体验;
  5. 部署依赖高,可考虑迁移到 Spring Boot 内嵌Tomcat。

总体来看,该项目非常适合作为JavaWeb入门者的实战练习,帮助理解Web应用的运行机制与分层思想。掌握其原理后,便可逐步过渡到现代微服务架构的学习。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询