
简介这份资源是基于JavaEE的图书管理系统完整源码包面向正在学习企业级Java开发的学生与初级开发者帮助其通过真实项目理解MVC与三层架构的落地方式。压缩包共93个文件、约6.62MB以java源码、jsp页面、xml配置、jar依赖和class文件为主涵盖Servlet、JDBC、Struts、Spring、MyBatis等核心技术的实际运用并包含登录、图书管理、系统管理等模块页面。已有146人学习下载适合作为课程设计或毕业设计的参考模板。读者可从中获取完整的项目目录结构、分层代码组织方式、数据库连接与事务处理思路以及前端jQuery与Bootstrap的集成示例便于导入IDE后对照调试快速掌握JavaEE项目从表现层到数据访问层的开发流程与常见配置要点。1. 从一份 Eclipse 时代的 JavaEE 图书管理系统压缩包说起如果你手上正好有一个java_ee_LibraryManagementSystem-master的压缩包解压后看到.classpath、.project、.settings这些 Eclipse 工程文件还有struts.xml、struts-sysadmin.xml、struts-bookadmin.xml、struts-user.xml四个 Struts 配置外加WebContent下一堆 JSP那你拿到的是一份典型的「SSH 时代」JavaEE 课设级项目。它不新但结构完整src下按bean、interceptor、action分包lmsDB目录里大概率躺着建库脚本WebContent里login.jsp、sysadmin.jsp、manager.jsp、history.jsp各司其职。这套东西能解决的核心问题是让你在一个真实可跑的多角色权限系统里把 Struts2 的 Action 分发、拦截器栈、JDBC 数据访问和 JSP 视图渲染串成一条线。适合谁适合正在做课程设计、需要一份能改能跑能讲清楚分层结构的 JavaEE 参考实现的人也适合想回头补一补「没有 Spring Boot 之前企业应用长什么样」的开发者。下面我按实际拆包、导库、改配置、跑起来的顺序把这份资源讲透。2. 拆包先看结构Struts2 多模块配置与三层架构怎么对应2.1 四个 struts 配置文件的分工逻辑拿到包先别急着往 IDE 里导先把src根目录下的四个 XML 打开对比。struts.xml是主配置通常只做全局常量、拦截器栈定义和include其他三个文件struts-sysadmin.xml、struts-bookadmin.xml、struts-user.xml分别对应系统管理员、图书管理员、普通用户三个角色的 Action 映射。这种按角色拆配置文件的做法在当年是很实用的权限隔离手段——不同角色能访问的 Action 在物理文件层面就分开了改一个角色的路由不会误伤另一个。常见做法是主配置里写struts constant namestruts.devMode valuetrue/ constant namestruts.i18n.encoding valueUTF-8/ include filestruts-sysadmin.xml/ include filestruts-bookadmin.xml/ include filestruts-user.xml/ /strutsstruts.devMode设成true是调试期必须的改完 XML 不用重启 Tomcat报错页面也会把完整堆栈打出来。struts.i18n.encoding设 UTF-8 是血泪经验不设的话 JSP 表单提交中文书名进数据库就是乱码。三个include的顺序不影响功能但建议按角色权限从高到低排排查问题时一眼能定位到是哪个角色的映射出了岔子。2.2 src 分包与三层架构的对应关系src下能看到bean、interceptor、action三个包这正好对应三层架构里的模型层、控制层和横切关注点。bean包里是实体类字段跟lmsDB里表的列一一对应比如 Book 类有bookId、bookName、author、publisher、stock这些属性。action包是 Struts2 的 Action 类每个类里一个方法对应一个请求方法返回字符串决定跳哪个 JSP。interceptor包放自定义拦截器最典型的就是登录校验拦截器——在 Action 执行前检查session里有没有用户信息没有就踢回loginError.jsp。数据访问层没有单独建包通常混在action里直接写 JDBC或者放在bean旁边一个dao包。你拿到包后先确认lmsDB目录里有没有.sql文件有的话直接拿去建库没有的话就得根据bean里的字段反推表结构。我一般会先看action里PreparedStatement的 SQL 语句把用到的表名和列名抄出来再补建表脚本。2.3 导入 Eclipse 前的环境检查清单这份工程带.classpath和.project说明原作者用的是 Eclipse。如果你用 IntelliJ IDEA直接Open项目根目录IDEA 会自动识别 Eclipse 工程并转换。但转换前先确认三件事JDK 版本、Tomcat 版本、数据库驱动。检查项常见值不匹配的后果JDK1.7 或 1.8高版本 JDK 编译 Struts2 老版本可能报Unsupported major.minorTomcat7 或 8Tomcat 10 的javax.*改成了jakarta.*老项目直接跑不起来MySQL 驱动mysql-connector-java-5.x用 8.x 驱动连 5.x 数据库要改 URL 参数Struts2 版本2.3.x 或 2.5.x2.5 之后struts.xml的 DTD 和包名有变化提示先看WEB-INF/lib下有没有 jar 包。如果 lib 是空的说明依赖没随包带出来你得自己按struts.xml里的 DTD 版本去补 Struts2 核心包和mysql-connector。3. 让系统跑起来建库、改连接、配 Tomcat 的完整链路3.1 从 lmsDB 目录还原数据库lmsDB这个目录名基本就是「library management system database」的缩写。进去看如果有.sql文件直接用命令行导入mysql -u root -p -e CREATE DATABASE lmsDB DEFAULT CHARACTER SET utf8mb4; mysql -u root -p lmsDB lmsDB/lmsDB.sql第一句建库时指定utf8mb4第二句把脚本灌进去。如果脚本里已经写了CREATE DATABASE第一句可以跳过。导入完执行SHOW TABLES;确认表都进去了通常会有user、book、borrow_record这几张核心表。user表里一般有个role字段区分 sysadmin、bookadmin、user 三种角色登录时靠这个字段决定跳哪个主页。如果lmsDB里没有.sql文件只有一堆.frm、.ibd文件那是 MySQL 的物理数据文件不能直接导入得找原作者要逻辑备份。这种情况我一般会根据action里的 SQL 反推建表语句下面是一个典型的 book 表还原示例CREATE TABLE book ( book_id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), stock INT DEFAULT 0, price DECIMAL(10,2) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;book_id设自增主键stock默认 0 防止借书时库存为负price用DECIMAL而不是FLOAT避免金额精度丢失。这些细节在还原时补上后面跑借阅流程才不容易出玄学问题。3.2 修改 JDBC 连接配置数据库建好后去src下找连接配置。Struts2 项目通常有两种写法一种是在struts.xml里配struts.properties或常量另一种是直接写个DBUtil类硬编码。搜jdbc:mysql这个关键字找到后改三处URL、用户名、密码。public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/lmsDB?useUnicodetruecharacterEncodingutf8useSSLfalse; private static final String USER root; private static final String PASSWORD your_password; public static Connection getConnection() throws SQLException { try { Class.forName(com.mysql.jdbc.Driver); } catch (ClassNotFoundException e) { throw new SQLException(MySQL 驱动未找到, e); } return DriverManager.getConnection(URL, USER, PASSWORD); } }useUnicodetruecharacterEncodingutf8这两个参数是中文不乱码的关键useSSLfalse是避免启动时一堆 SSL 警告。驱动类名com.mysql.jdbc.Driver对应 5.x 版本如果你用的是 8.x 驱动要改成com.mysql.cj.jdbc.DriverURL 里还得加serverTimezoneAsia/Shanghai否则连库直接抛时区异常。改完这些写个main方法调一下getConnection()能打印出连接对象就说明数据库这关过了。3.3 Tomcat 部署与上下文路径Eclipse 里配 Tomcat 的步骤是Window Preferences Server Runtime Environments添加 Tomcat 安装目录然后把项目Add and Remove到服务器实例上。关键点是WebContent目录会被识别为 Web 根WEB-INF/web.xml里配的struts2过滤器负责拦截所有请求。filter filter-namestruts2/filter-name filter-classorg.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter/filter-class /filter filter-mapping filter-namestruts2/filter-name url-pattern/*/url-pattern /filter-mappingStrutsPrepareAndExecuteFilter是 Struts2 2.1 之后的标准过滤器url-pattern写/*表示所有请求都过 Struts2 的路由。启动 Tomcat 后访问http://localhost:8080/项目名/login.jsp能出登录页就说明部署成功。如果 404检查WebContent下index.jsp是否存在、web.xml的welcome-file有没有配错如果 500 且报ClassNotFoundException基本是WEB-INF/lib下缺 Struts2 的 jar。3.4 登录流程与角色跳转验证系统跑起来后第一件事是验证登录链路。打开login.jsp看表单action指向哪个 Action通常是loginAction或userLogin。提交后 Action 里会拿用户名密码去user表查查到就把用户对象塞进session然后根据role字段返回不同结果字符串。public String execute() { User user userService.login(username, password); if (user null) { return loginError; } ServletActionContext.getRequest().getSession().setAttribute(currentUser, user); if (sysadmin.equals(user.getRole())) { return sysadmin; } else if (bookadmin.equals(user.getRole())) { return bookadmin; } return user; }返回的字符串在struts-*.xml里映射到不同 JSPsysadmin跳sysadmin.jspbookadmin跳manager.jspuser跳index.jsp。验证时三个角色各登一次确认跳转正确、session里能取到用户信息。如果登录后一直跳回loginError.jsp先查数据库里密码是不是明文存的再查 Action 里有没有对密码做 MD5 加密而数据库存的是明文——这种不一致是课设项目最常见的翻车点。4. 避坑与排查这份老工程最容易卡住的五个地方4.1 启动报 ClassNotFoundException: com.mysql.jdbc.Driver现象是 Tomcat 启动或第一次访问数据库时抛ClassNotFoundException堆栈指向DBUtil的Class.forName。原因就一个WEB-INF/lib下没有 MySQL 驱动 jar或者驱动版本和代码里写的类名对不上。解决方法是确认lib下有mysql-connector-java-5.1.x.jar如果用的是 8.x 驱动把Class.forName(com.mysql.jdbc.Driver)改成com.mysql.cj.jdbc.Driver同时 URL 加serverTimezone参数。改完记得在 Eclipse 里Project Clean一下让build/classes重新编译。4.2 JSP 中文乱码数据库里存进去是问号现象是页面上输入中文书名提交后数据库里变成???。原因有两层JSP 页面没声明pageEncoding或者数据库连接没带字符集参数。解决分三步每个 JSP 头部加% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %DBUtil的 URL 加useUnicodetruecharacterEncodingutf8建库时用utf8mb4而不是默认的latin1。三处都改完还乱码就去 MySQL 命令行执行SHOW VARIABLES LIKE character%;看服务端字符集character_set_server不是utf8mb4的话得改my.ini重启服务。4.3 Struts2 报 There is no Action mapped for namespace现象是访问某个功能页时 404控制台提示There is no Action mapped for namespace [/] and action name [xxx]。原因是struts-*.xml里没配这个 Action 的映射或者include没生效。排查顺序先确认struts.xml里include了对应的子配置文件再确认子配置里package的namespace和action的name跟 JSP 表单的action属性完全一致大小写敏感最后确认struts.devMode是true这样启动时会打印所有已加载的 Action 映射一眼能看出缺哪个。4.4 拦截器把登录请求也拦了导致死循环现象是访问login.jsp提交后一直跳回登录页或者直接报重定向次数过多。原因是自定义的登录校验拦截器url-pattern配成了/*把loginAction本身也拦了。解决是在拦截器里放行登录相关的 Action或者用 Struts2 的excludeMethods参数排除login方法。常见写法是在struts.xml的拦截器栈里加interceptor-ref nameloginInterceptor param nameexcludeMethodslogin,execute/param /interceptor-refexcludeMethods里列的方法名不会被拦截多个用逗号隔开。改完重启 Tomcat确认登录请求能正常进 Action 而不是被踢回来。4.5 Tomcat 端口占用或项目上下文冲突现象是启动 Tomcat 报Address already in use: JVM_Bind或者访问时跳到另一个项目的页面。原因是 8080 端口被别的进程占了或者 Eclipse 里同时部署了多个项目导致上下文路径冲突。解决命令行执行netstat -ano | findstr 8080找到占用进程 PID任务管理器里结束它或者在 Eclipse 的 Tomcat 配置里把 HTTP 端口改成 8081。上下文冲突的话双击 Tomcat 实例在Modules标签页里确认只有当前项目被部署Path列显示的就是访问路径。5. 进阶改造把 JDBC 换成连接池并加一层 DAO5.1 为什么课设级 JDBC 需要连接池原工程每个 Action 里DBUtil.getConnection()一次用完close()请求量一上来就频繁创建销毁连接数据库端Threads_connected飙高。常见做法是引入 Druid 或 C3P0 连接池把连接的创建和回收交给池管理。以 Druid 为例在src下加druid.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/lmsDB?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai usernameroot passwordyour_password initialSize5 maxActive20 maxWait3000initialSize是启动时预建的连接数maxActive是池上限maxWait是拿不到连接时的最长等待毫秒数。这三个参数按你机器配置和并发预期调课设场景initialSize5、maxActive20足够。然后在DBUtil里改成从DruidDataSourceFactory拿连接public class DBUtil { private static DataSource dataSource; static { try { Properties props new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream(druid.properties)); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new ExceptionInInitializerError(连接池初始化失败: e.getMessage()); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }静态代码块保证池只初始化一次getConnection()从池里借连接close()时 Druid 会自动把连接还回池里而不是真关掉。改完压一下登录接口看日志里连接创建次数是不是明显下降。5.2 抽一层 DAO 把 SQL 从 Action 里挪出去原工程 SQL 散落在各个 Action 里改一个表结构要翻好几个文件。抽 DAO 的思路是每个实体一个 DAO 类Action 只调 DAO 方法不碰PreparedStatement。以 Book 为例public class BookDao { public ListBook findAll() throws SQLException { String sql SELECT book_id, book_name, author, publisher, stock, price FROM book; ListBook list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Book b new Book(); b.setBookId(rs.getInt(book_id)); b.setBookName(rs.getString(book_name)); b.setAuthor(rs.getString(author)); b.setPublisher(rs.getString(publisher)); b.setStock(rs.getInt(stock)); b.setPrice(rs.getBigDecimal(price)); list.add(b); } } return list; } }try-with-resources保证Connection、PreparedStatement、ResultSet三个资源自动关闭不用手写finally块。rs.getBigDecimal(price)对应数据库的DECIMAL类型避免getDouble的精度问题。Action 里改成new BookDao().findAll()代码量少一半后面换 MyBatis 或 JPA 时也只需要改 DAO 层Action 不动。5.3 用 JUnit 验证 DAO 层别每次都靠点页面改完 DAO 后写个 JUnit 测试类跑一遍增删查改比每次启动 Tomcat 点页面快得多public class BookDaoTest { private BookDao dao new BookDao(); Test public void testFindAll() throws SQLException { ListBook books dao.findAll(); assertNotNull(books); System.out.println(查到 books.size() 本书); } Test public void testInsertAndDelete() throws SQLException { Book b new Book(); b.setBookName(测试书籍); b.setAuthor(测试作者); b.setStock(1); int id dao.insert(b); assertTrue(id 0); assertEquals(1, dao.deleteById(id)); } }testFindAll验证查询链路通不通testInsertAndDelete验证写操作和事务。跑测试前确认druid.properties在test资源目录下也能被加载否则会报NullPointerException。这套测试跑通后再改 DAO 里的 SQL 就有底气了——改完跑一遍绿了就说明没破坏原有功能。从那以后我每次动数据库相关的代码都强制先跑一遍 DAO 测试再启动 Tomcat省得在页面上反复点。希望这份拆解能帮你把这份老工程顺利跑起来并在它基础上做出自己的改造。本文还有配套的精品资源点击获取