
简介这是一份基于 JSPServlet 的企业人事管理系统完整源码包面向 Java 初学者、毕业设计学生及小型企业项目参考。压缩包共 229 个文件约 5.8MB主要包含 88 个 JSP 页面、18 个 Java 源码与对应 class 文件以及 16 个 jar 依赖库和 28 个 tld 标签描述文件可支撑 MVC 分层架构与前后端交互另有 32 张 JPG、11 张 GIF 图片素材4 个 XML 配置、properties 配置和数据库文件mdf/ldf、db 等便于快速搭建运行环境。已有 176 人学习下载。读者可从中获取完整项目源码、数据库脚本、配置说明与页面素材既能学习 JSP/Servlet 开发流程、用户权限与人事业务模块设计也可直接用于毕业设计或小规模企业管理场景的二次开发参考。1. JSP企业人事管理系统老技术栈里的完整练手范本一个打包下载的 JSP 企业人事管理系统源码摆在你面前时最该问的不是“JSP 是不是过时了”而是“这套代码能不能跑起来、里面的分层方式值不值得抄”。JSP 技术本身确实不是新东西但它至今仍是很多高校课程设计、毕业设计和中小型企业内部系统的底子原因很简单Servlet 容器天然处理 HTTP 生命周期JSP 页面直接嵌 Java 代码部署到 Tomcat 就能开张。这套源码我拆过一遍里面覆盖了登录认证、员工信息 CRUD、部门管理、薪资查询这类人事系统的标准模块用的是 JSP Servlet JavaBean MySQL 的经典组合配合 SQL 脚本和部署说明打包在一起。适合两类人一是正在做 Java Web 课程设计、需要一份能跑通做参考的学生二是想快速搭一个内部人事后台的从业者。这周我把它从环境配置到源码走读完整过了一遍下面直接说怎么部署、怎么改、以及最容易翻车的那几个位置。2. 架构认知JSP Servlet JavaBean 的经典三层是怎么组织的2.1 这套源码的文件结构与模块边界解压之后你会看到典型的 MyEclipse / Eclipse 工程目录。src 下是 Servlet 和 JavaBeanWebRoot或 webroot下是 JSP 页面、WEB-INF、配置文件。给个典型清单路径内容作用src/com/company/servletEmployeeServlet、LoginServlet、DeptServlet请求入口、控制页面跳转src/com/company/beanEmployee、Department、User数据模型对应数据库表src/com/company.daoEmployeeDao、DeptDaoJDBC 数据访问层src/com/company.utilDBUtil.java数据库连接工具类WebRoot/WEB-INF/web.xml部署描述符配置 Servlet 映射、欢迎页WebRoot/login.jsp、index.jsp登录页和主框架页前端展示sql/company.sql 或 *.sql建库建表脚本导入 MySQL 生成表结构和初始数据这个分层的价值在于边界清晰JSP 只负责展示Servlet 只做路由和控制JavaBean 承载数据DAO 屏蔽 SQL 细节。用现在的话说这就是最朴素的 MVC只是没有 Spring 帮你管对象。拆这套源码的时候你会发现所有 Servlet 都继承 HttpServletdoPost 里拿 request 参数、调 DAO、往 request 或 session 里塞结果、forward 到 JSP。这套写法虽然原始但每一步都看得见对理解请求生命周期非常有帮助。2.2 数据流转一次“查询员工列表”请求背后发生了什么从浏览器输入http://localhost:8080/hrms/EmployeeServlet?actionlist开始到页面渲染出表格中间走了一条固定的链路。web.xml 里配置的映射决定请求落到哪个 ServletServlet 根据 action 参数决定调哪个 DAO 方法DAO 通过 DBUtil 拿 Connection 执行 SQL结果集被封装成 ListEmployee塞进 request 的 attribute最后 forward 到 list.jsp由 JSP 用c:forEach或脚本片段遍历输出。这里特别值得注意的一个设计细节是请求参数action。这套源码用一个 Servlet 承载多个操作比如同一个 EmployeeServlet 处理增删改查靠actionadd、actionupdate、actiondelete、actionlist来分流。优缺点都很明显优点是类数量少适合课程设计的篇幅缺点是方法一多if-else 链会变得很臃肿我们后面改造时会讲怎么优化。2.3 JSP 页面里两种写法的取舍如果用记事本打开 index.jsp你会同时看到两种风格的代码一种是%开头的 Java 脚本片段直接写在页面里做循环另一种是用 JSTL 标签库的c:forEach。老项目里前者居多新一点的项目里后者居多。这套源码大概率两种都混着。脚本片段的写法直观对初学者友好但页面里 Java 代码越多维护越痛苦改个样式都可能碰到业务逻辑。JSTL 则把循环和判断收敛成标签页面干净但需要引入 jstl.jar 和 standard.jar并且在 web.xml 里不额外配置也能用只要页面里% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %声明就行。拆源码时如果看到报错说找不到标签先确认 lib 下有没有这两个 jar。3. 部署这套源码从 JDK 到 Tomcat 到 MySQL 的完整步骤3.1 环境版本选择这一步定生死部署 JSP 老项目环境版本不是越新越好反而要先看源码用的什么底子。血泪经验是JDK 8 Tomcat 8.5 是一对黄金搭档MySQL 5.7 对老驱动和 SQL 语法的兼容性最好。如果你本机是 JDK 11 甚至 17Tomcat 8.5 依然能跑但如果你硬上 Tomcat 10那 web.xml 头部的命名空间都要换Servlet 类的包名从 javax.servlet 变成 jakarta.servlet老源码直接编译不过这不是配置能解决的是 API 层面的断代。先确认 JDK 是否正确配置。命令行里执行java -version javac -version echo %JAVA_HOME%如果 javac 不识别说明 PATH 里没配上 JDK 的 bin 目录。常见做法是把JAVA_HOME指向 JDK 安装根目录比如C:\Program Files\Java\jdk1.8.0_202然后在 PATH 里追加%JAVA_HOME%\bin。配置完后重开命令行窗口验证。这步看着基础但十个部署问题里至少有两个卡在环境变量上——特别是你之前装过多个 JDK 版本时。Tomcat 这边下载 zip 版解压就行不需要安装。解压后 bin 目录下的 startup.bat 是启动脚本catalina.bat 是核心控制脚本。第一次启动前建议改一下 conf/server.xml 里的端口尤其是 8080 被占用的时候Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /把 8080 改成 8088 或 9090改完重启 Tomcat 才生效。启动后打开浏览器访问http://localhost:8088看到 Tomcat 默认首页就算环境通了。这一步先把容器跑起来别急着丢项目环境不通后面全是噪音。3.2 导入数据库别直接用外部工具乱执行解压目录里的 sql 文件夹通常有一个 company.sql 或 hrms.sql。这里要强调的是不要双击 .sql 文件用文本编辑器打开复制粘贴到 Navicat 查询窗口执行因为脚本里可能包含DELIMITER $$这种存储过程定义也可能包含中文注释客户端编码不对就会中途报错。更稳的做法是在命令行里执行编码用 UTF-8mysql -uroot -p --default-character-setutf8 CREATE DATABASE IF NOT EXISTS hrms DEFAULT CHARACTER SET utf8; USE hrms; SOURCE D:/download/hrms/sql/company.sql;执行完用SHOW TABLES;验证一下能看到 sys_user、employee、department 之类的表就说明导入成功。如果提示某个字段不存在别急着改脚本先看是不是导错了数据库——很多人忘了USE hrms结果表建到了别的库下面。检查SELECT DATABASE();确认当前库。这种命令行方式的好处是错误提示直接、编码可控而且能保留脚本里的注释后续要手动补数据也方便。导入完成后用SELECT COUNT(*) FROM sys_user;确认初始管理员账号在一般会有一条 admin 记录密码可能在脚本里写死为 123456 或 admin也可能封装在 MD5 后的密文里。3.3 数据源配置JDBC 连接串里的那几个关键参数老 JSP 项目的数据库连接配置通常集中在 src 下的 DBUtil.java 里或者 conf 目录下。找到核心配置那几行一般长这样Class.forName(com.mysql.jdbc.Driver); String url jdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8; String user root; String password 123456;这里最容易改错的是 Driver 类名和 URL 格式。如果用 MySQL 5.x驱动类是com.mysql.jdbc.Driver如果你本机是 MySQL 8.x就得换成com.mysql.cj.jdbc.Driver并且 URL 后面要加serverTimezoneAsia/Shanghai否则启动时百分之百报Server returns invalid timezone的错。我的习惯是把密码统一改成自己本机的 root 密码然后加一个useSSLfalse参数避免 MySQL 8 默认 SSL 握手报错String url jdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai;注意这里有两个坑一是字符编码必须和 JSP 页面里pageEncodingUTF-8保持一致否则中文姓名和部门名称在库里就是乱码这不是显示层问题是数据源头就坏了二是如果项目用的数据库名不是 hrms要同步改 URL 里的库名。改完 DBUtil 需要重新编译如果你用 Eclipse 或 IDEA 直接跑项目会自动编译如果是手工部署到 Tomcat那就得重新 build 出 class 文件再覆盖到 WEB-INF/classes 下。3.4 部署到 TomcatIDE 内外两条路线有 IDE 就走 IDE 路线。以 Eclipse 为例右键项目选择 Properties → Targeted Runtimes勾选你的 Tomcat 8.5然后右键项目 → Run As → Run on Server。IDEA 里则是配置 Tomcat ServerDeployment 标签页点 号选 ArtifactApplication context 填/hrms这样访问路径就是http://localhost:8080/hrms。没有 IDE 或想搞清楚手工部署的人在后面直接把项目导出成 war 包或者把 WebRoot 下的内容连同编译后的 classes 一起拷贝到 Tomcat 的 webapps 下改成 hrms 文件夹名。Tomcat 是支持热部署目录的启动时会自动加载 webapps 下的应用。手工部署时尤其注意 WEB-INF/classes 里要有编译好的 .class 文件lib 里有 mysql-connector-java.jarWEB-INF/lib 下还要有 jstl.jar 和 standard.jar。少了 jstl.jar 的典型症状是 JSP 页面报The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved这是缺包不是写错代码。启动 Tomcat 后访问http://localhost:8080/hrms/login.jsp看到登录页就说明部署成功了。如果 Tomcat 起不来先去 logs/catalina.out 或 logs/localhost.log 里看异常堆栈这是唯一靠谱的排错起点别瞎猜。4. 拆核心模块源码登录认证、SQL 防注入与 CRUD 的标准写法4.1 登录逻辑Session 与请求分发的配合登录是整个系统的入口这里的代码质量决定了后续所有页面的鉴权方式。典型的 LoginServlet 会这么写protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao userDao new UserDao(); User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { request.getSession().setAttribute(loginUser, user); response.sendRedirect(index.jsp); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }逻辑很直白拿参数、查库、判断、跳转。有一个值得留意的细节是成功之后用sendRedirect失败用forward。为什么要区分因为sendRedirect是重定向浏览器地址栏会变成 index.jsp同时会重新发起一次请求之前的 request 对象失效但 session 还在forward是服务端跳转request 里的属性error 信息能带到 JSP 里。如果登录失败你也用重定向那 error 属性就丢了页面上永远看不到错误提示。这个差异在面试里也常被问到建议对照源码多读两遍。4.2 过滤器与未登录拦截很多项目缺的一环另一个值得拆的类是权限过滤器。如果这套源码里带了LoginFilter它的作用就是拦截所有请求检查 session 里有没有登录标记。核心逻辑是这样public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(); String uri req.getRequestURI(); if (uri.contains(login.jsp) || uri.contains(LoginServlet) || uri.endsWith(.js) || uri.endsWith(.css) || uri.endsWith(.jpg)) { chain.doFilter(request, response); return; } if (session null || session.getAttribute(loginUser) null) { resp.sendRedirect(login.jsp); } else { chain.doFilter(request, response); } }这个过滤器要注册到 web.xml 里并配置url-pattern/*/url-pattern拦截所有路径。白名单那段逻辑很重要登录页本身、登录接口、静态资源JS、CSS、图片必须放行否则页面样式全丢而且会陷入“访问 login.jsp 也被拦去 login.jsp”的死循环。你可以检查源码里的过滤器有没有处理静态资源放行很多时候原项目没写这层我拆包时会在讲解里补上这段并提醒加过滤器时把白名单做全。4.3 DAO 层与 PreparedStatementSQL 注入防线这家公司的 DAO 层几乎都用 JDBC 直连。查询员工列表的典型逻辑如下public ListEmployee findAll() { ListEmployee list new ArrayListEmployee(); Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); String sql SELECT id, emp_no, name, dept_id, phone, hire_date FROM employee; ps conn.prepareStatement(sql); rs ps.executeQuery(); while (rs.next()) { Employee emp new Employee(); emp.setId(rs.getInt(id)); emp.setEmpNo(rs.getString(emp_no)); emp.setName(rs.getString(name)); emp.setDeptId(rs.getInt(dept_id)); emp.setPhone(rs.getString(phone)); list.add(emp); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(rs, ps, conn); } return list; }如果按姓名搜索SQL 里会带WHERE name LIKE ?而?参数通过ps.setString(1, % keyword %)传进去而不是字符串拼接。这是一个重要的分水岭拼接 SQL 容易出注入漏洞PreparedStatement预编译会把单引号转义掉。我拆过的源码里如果发现有的地方是拼接建议你改掉把这个改动写进自查清单。4.4 添加与更新员工事务与脏数据风险EmployeeServlet 里的 add 操作一般会做两件事接收表单参数、检查必填项、调 DAO 插入。这里最常见的缺口是事务处理——如果需要同时插入 employee 表和 employee_detail 表员工主表 详细履历表而 DAO 里两次分别获取连接执行 SQL那中间任何一条失败数据就半截落库了。正确的做法是在 Service 层包一个事务Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); EmployeeDao.save(conn, employee); EmployeeDetailDao.save(conn, detail); conn.commit(); } catch (SQLException e) { conn.rollback(); throw new RuntimeException(保存员工失败, e); } finally { conn.close(); }但很多课程设计级源码压根没有 Service 层直接在 Servlet 里连调两个 DAO事务自然就没了。你拿到源码后可以搜一下setAutoCommit如果一次都没出现说明整个项目都是单表操作不存在跨表事务这也是它的一个简化边界写报告时值得提一句。5. 部署避坑从 JDK 到字符集最常见的八个启动坑5.1 404 或跳转到 Tomcat 首页现象启动成功后访问http://localhost:8080/项目名/login.jsp返回 404或者页面跑到 Tomcat 默认首页去了。原因应用没有成功部署。要么是 war 包没放进 webapps要么是项目名输错要么是 IDEA/Eclipse 的 Application context 没配对。Tomcat 默认首页说明你访问的是根路径而应用挂在/hrms下。解决确认访问路径带项目名。IDEA 的 Deployment 里检查 Application context 是否为/hrms手工部署时确认 webapps 下有项目文件夹并且里面有 WEB-INF 目录。用http://localhost:8080/hrms/login.jsp重新访问。5.2 启动报 ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat 启动日志里出现找不到 MySQL 驱动类的错误。原因mysql-connector-java.jar 没放进 WEB-INF/lib或者驱动类名写成了 5.x 版本但实际驱动 jar 是 8.x 的。解决先看你用的连接 jar 版本。5.x 版本对应com.mysql.jdbc.Driver8.x 版本对应com.mysql.cj.jdbc.Driver。把 jar 放到 WEB-INF/lib 下并重新部署。如果看到Public Key Retrieval is not allowed加参数allowPublicKeyRetrievaltrue。5.3 页面中文全是问号或乱码现象登录后员工列表里所有中文姓名显示为???或者 JSP 页面标题乱码。原因三层编码不一致。JSP 文件本身没存成 UTF-8、pageEncoding没设对、数据库连接串没带characterEncodingutf8、数据库和表的排序规则不是 utf8任一环节断掉都是乱码。解决用下面一条硬规则排查——JSP 文件开头加% page languagejava pageEncodingUTF-8 contentTypetext/html; charsetUTF-8%DBUtil 连接串带useUnicodetruecharacterEncodingutf8数据库建表脚本确认DEFAULT CHARSETutf8。三处统一后通常能解决。5.4 端口被占用Tomcat 启动一闪而过现象双击 startup.bat 后窗口一闪就消失http://localhost:8080打不开。原因8080 端口被其他进程占用或者之前启动的 Tomcat 没关干净。命令行窗口一闪而过是因为没有保留错误输出。解决先用netstat -ano | findstr 8080看占用进程要么杀掉对应 PID要么改 Tomcat 的 server.xml 端口。更稳的启动方式是在命令行手动执行catalina.bat run这样日志直接打在窗口里能直接看到报错原因。5.5 HTTP 状态 500JSP 里脚本片段报空指针现象页面能打开但列表区域报NullPointerException堆栈指向 JSP 里的 for 循环。原因Servlet 里request.setAttribute(empList, list)时 list 是 null或者往 session 里存的对象类型不匹配。常见场景是请求路径没带actionlistServlet 没走查询分支导致 attribute 没设置。解决访问路径补齐参数比如EmployeeServlet?actionlist。检查 Servlet 里查询后是否真的把 list 塞进了 request以及 JSP 里取值时 key 是否和 Servlet 里 set 的一致——这是最典型的低级 bug用浏览器的 URL 和 Servlet 代码对一遍就能定位。5.6 登录成功但跳回登录页现象输入正确账号密码点击登录页面闪一下后回到 login.jsp。原因过滤器拦截了 index.jsp 或后续页面的请求而 session 里没有登录标记。可能是登录成功后sendRedirect的路径写错导致新一轮请求没带 session也可能过滤器的逻辑判断有问题——有些原项目判断条件是session.getAttribute(loginUser) ! null但登录时存的是user这个 key。解决登录 Servlet 里 set 的 key 和过滤器里 get 的 key 必须完全一致。全局搜索setAttribute和getAttribute两处代码确认是同一个字符串。另外sendRedirect的目标路径要是相对路径或完整上下文路径比如index.jsp或/hrms/index.jsp写成了绝对路径会导致新请求绕过你的应用上下文。6. 验收与改造把这份源码变成能写进简历的项目先把验收标准定下来。一个 JSP 人事管理系统能不能算“跑通”我会走一遍这五个检查登录退出是否正常流转员工信息的增删改查是否落地到数据库重启 Tomcat 后数据还在部门与员工有没有关联删除部门时是否误伤了员工数据三个及以上页面的中文显示无乱码最后是直接访问受保护页面能否被拦截回登录页。这几项过完才敢说这份源码能作为基础盘往下走。改造的优先级我建议先接手一个影响面最大但改动最小的把 JDBC 连接换成连接池。Apache DBCP 或 C3P0 都行DBCP2 配置更简洁。核心变化在 DBUtilprivate static BasicDataSource dataSource null; static { dataSource new BasicDataSource(); dataSource.setDriverClassName(com.mysql.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8useSSLfalse); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(5); dataSource.setMaxTotal(20); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }原来每次操作都DriverManager.getConnection现在改成从池里借连接性能提升是次要的重点是让 DAO 层在并发查询时不会频繁握手、也不会把连接漏关。改完用ab或者 Jmeter 简单压一下哪怕只有 50 个并发差异也很明显。另一个性价比高的改造是给 EmployeeServlet 的分流逻辑引入一个简单的命令模式或者至少把action的分支抽到一个 switch 里集中管理扩展性会好很多写简历时也能说是“重构了请求路由”。如果想要一个能现场演示、又不太需要新增代码的功能给员工列表加一个导出 CSV 是最合适的。新建一个ExportServlet在doGet里查询所有员工设置响应头Content-Disposition: attachment; filenameemployees.csv然后直接往输出流里写逗号分隔的文本即可完全不需要引入 POI。这个功能 20 行代码搞定但演示效果非常直观而且面试时可以说“通过导出功能加深了对响应头与输出流的理解”。从那以后我每次拿到这类打包源码都会先强制走一遍“环境匹配 → 数据库脚本导入 → 编译部署 → 登录走查 → 读核心 Servlet”这五步缺一步都容易在中途把时间耗在查环境上。这份源码本身不是多复杂的系统但它把 JSP 时代的标准组织方式和坑都沉淀下来了你把它完整跑通并改掉其中两处硬伤这笔时间就花得值。希望帮到你。本文还有配套的精品资源点击获取