1. 项目概述
这个JavaWeb实训作业的图书管理系统是一个典型的JSP+Servlet+MySQL技术栈实现的教学级项目。作为计算机专业学生的必修课程作业,它涵盖了从数据库设计到前端展示的完整开发流程。我在指导学生的过程中发现,很多团队往往在前端交互和细节完善上失分严重,这正是Day4需要重点攻克的部分。
系统采用经典的三层架构:JSP负责视图展示,Servlet处理业务逻辑,MySQL存储图书、用户等核心数据。实训作业通常要求实现图书的增删改查、用户登录、借阅管理等基础功能,但要想拿到高分,必须在JSP层的交互体验和功能完整性上下功夫。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能实现要点
2.1 数据库连接优化
使用DBCP连接池替代直接连接,在web.xml中配置:
xml复制<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/LibraryDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
在context.xml中配置具体参数:
xml复制<Resource
name="jdbc/LibraryDB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"
username="root"
password="123456"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/library?useSSL=false&serverTimezone=UTC"/>
注意:MySQL 8.0+需要添加时区参数,否则会出现连接异常
2.2 JSP页面功能增强
2.2.1 图书查询分页实现
在Servlet中处理分页参数:
java复制int page = 1;
int recordsPerPage = 5;
if(request.getParameter("page") != null)
page = Integer.parseInt(request.getParameter("page"));
List<Book> books = bookDAO.viewAllBooks(
(page-1)*recordsPerPage,
recordsPerPage
);
int noOfRecords = bookDAO.getNoOfRecords();
int noOfPages = (int) Math.ceil(noOfPages * 1.0 / recordsPerPage);
request.setAttribute("bookList", books);
request.setAttribute("noOfPages", noOfPages);
request.setAttribute("currentPage", page);
JSP页面显示分页控件:
jsp复制<c:if test="${noOfPages > 1}">
<div class="pagination">
<c:forEach begin="1" end="${noOfPages}" var="i">
<c:choose>
<c:when test="${currentPage eq i}">
<a class="active">${i}</a>
</c:when>
<c:otherwise>
<a href="bookList?page=${i}">${i}</a>
</c:otherwise>
</c:choose>
</c:forEach>
</div>
</c:if>
2.2.2 表单验证增强
使用JQuery+正则表达式实现客户端验证:
javascript复制$("#addBookForm").validate({
rules: {
isbn: {
required: true,
pattern: /^\d{3}-\d-\d{3}-\d{5}-\d$/
},
publishDate: {
required: true,
date: true
}
},
messages: {
isbn: {
pattern: "请输入有效的ISBN格式(如:978-7-302-33041-5)"
}
}
});
2.3 会话管理优化
2.3.1 登录状态拦截器
创建LoginFilter实现Filter接口:
java复制public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURI = request.getContextPath() + "/login";
boolean loggedIn = session != null && session.getAttribute("user") != null;
boolean loginRequest = request.getRequestURI().equals(loginURI);
if (loggedIn || loginRequest) {
chain.doFilter(request, response);
} else {
response.sendRedirect(loginURI);
}
}
在web.xml中配置过滤路径:
xml复制<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.library.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
<url-pattern>/user/*</url-pattern>
</filter-mapping>
3. 数据库设计优化
3.1 表结构设计
sql复制CREATE TABLE `books` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`isbn` varchar(20) NOT NULL,
`title` varchar(100) NOT NULL,
`author` varchar(50) NOT NULL,
`publisher` varchar(50) NOT NULL,
`publish_date` date NOT NULL,
`price` decimal(10,2) NOT NULL,
`quantity` int(11) NOT NULL DEFAULT '0',
`cover_image` varchar(255) DEFAULT NULL,
`description` text,
PRIMARY KEY (`id`),
UNIQUE KEY `isbn_UNIQUE` (`isbn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(45) NOT NULL,
`password` varchar(64) NOT NULL,
`salt` varchar(20) NOT NULL,
`email` varchar(45) NOT NULL,
`phone` varchar(20) DEFAULT NULL,
`role` enum('admin','user') NOT NULL DEFAULT 'user',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username_UNIQUE` (`username`),
UNIQUE KEY `email_UNIQUE` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `borrow_records` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`book_id` int(11) NOT NULL,
`borrow_date` date NOT NULL,
`return_date` date DEFAULT NULL,
`due_date` date NOT NULL,
`status` enum('borrowed','returned','overdue') NOT NULL DEFAULT 'borrowed',
PRIMARY KEY (`id`),
KEY `fk_user_idx` (`user_id`),
KEY `fk_book_idx` (`book_id`),
CONSTRAINT `fk_book` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`),
CONSTRAINT `fk_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 密码安全存储
使用SHA-256加盐哈希存储密码:
java复制public class PasswordUtil {
private static final String ALGORITHM = "SHA-256";
public static String generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
public static String hashPassword(String password, String salt) {
try {
MessageDigest md = MessageDigest.getInstance(ALGORITHM);
md.update(salt.getBytes());
byte[] hashedPassword = md.digest(password.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hashedPassword);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
4. 常见问题解决方案
4.1 JSP页面中文乱码
在Servlet中添加:
java复制response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
在JSP页面顶部添加:
jsp复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
在MySQL连接URL中添加参数:
java复制jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=UTF-8
4.2 IDEA中JSP引用跳转问题
-
确保项目已正确配置Facets:
- File → Project Structure → Facets
- 添加Web Facet并指定web目录
-
检查JSP支持是否启用:
- Settings → Languages & Frameworks → JSP
- 确保"JSP Support"已勾选
-
清除缓存并重启IDEA:
- File → Invalidate Caches / Restart
4.3 数据库连接超时
在连接池配置中添加以下参数:
xml复制<Resource
...
validationQuery="SELECT 1"
testOnBorrow="true"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"
minEvictableIdleTimeMillis="60000"
/>
5. 项目部署与测试
5.1 Tomcat部署配置
在server.xml中添加Context配置:
xml复制<Context docBase="LibrarySystem" path="/library"
reloadable="true" source="org.eclipse.jst.jee.server:LibrarySystem"/>
配置数据源:
xml复制<Resource name="jdbc/LibraryDB"
auth="Container"
type="javax.sql.DataSource"
...
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
/>
5.2 单元测试示例
使用JUnit测试DAO层:
java复制public class BookDAOTest {
private BookDAO bookDAO;
private Connection connection;
@Before
public void setUp() throws Exception {
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/library_test",
"root",
"password"
);
bookDAO = new BookDAOImpl(connection);
}
@Test
public void testAddBook() {
Book book = new Book();
book.setIsbn("978-7-302-33041-5");
book.setTitle("Java编程思想");
// 设置其他属性...
boolean result = bookDAO.addBook(book);
assertTrue(result);
Book retrieved = bookDAO.getBookByIsbn(book.getIsbn());
assertEquals(book.getTitle(), retrieved.getTitle());
}
@After
public void tearDown() throws Exception {
connection.rollback();
connection.close();
}
}
5.3 压力测试建议
使用JMeter进行并发测试:
- 创建线程组:设置线程数(用户数)、循环次数
- 添加HTTP请求:配置登录请求参数
- 添加查看结果树和聚合报告
- 重点监控指标:
- 平均响应时间
- 错误率
- 吞吐量(requests/sec)
提示:测试前确保数据库中有足够多的测试数据(至少1000本图书和100个用户)
6. 项目扩展建议
6.1 前端优化方向
- 使用Bootstrap 5实现响应式布局
- 添加图书封面图片上传和显示功能
- 实现基于AJAX的动态搜索建议
- 使用Chart.js展示借阅统计图表
6.2 后端增强功能
- 添加图书预约功能
- 实现借阅到期邮件提醒
- 开发RESTful API供移动端调用
- 添加操作日志记录功能
6.3 安全加固措施
- 实现CSRF防护:
jsp复制<input type="hidden" name="csrfToken" value="${sessionScope.csrfToken}">
- 添加XSS防护过滤器:
java复制public class XSSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(new XSSRequestWrapper((HttpServletRequest) req), res);
}
}
- 密码策略增强:
- 最小长度8位
- 必须包含大小写字母和数字
- 定期强制修改密码
在实际项目开发中,我发现很多学生在JSP页面逻辑处理上容易犯两个典型错误:一是将过多业务逻辑写在JSP中,二是没有处理好页面之间的数据传递。正确的做法应该是保持JSP只负责展示,所有业务逻辑都放在Servlet或Service层处理。对于页面跳转时的数据传递,使用request.setAttribute()比session更合适,可以避免会话数据堆积。
