简介:这个人事管理Web项目用纯Java Web技术实现,前端用JSP渲染登录页loginForm.jsp和主页index.jsp,配合CSS样式、JavaScript交互脚本和图片资源;后端由Servlet处理员工、部门、岗位等业务逻辑,数据存入MySQL,通过Druid连接池高效访问数据库。项目自带hrm.sql建表语句,定义了完整的员工信息结构;log4j.properties支持日志记录,druid.properties配置数据库连接参数;src目录下是com包结构的Java源码,编译后class文件放在classes目录;WebContent包含标准WEB-INF结构,兼容Tomcat 9.0;所有依赖JAR已打包在内,导入Eclipse或IDEA后无需额外下载库即可运行调试。部署时只需将项目发布到Tomcat,启动服务就能访问登录界面,适合Java初学者练手和教学演示。
1. 项目概述:为什么这套人事系统源码值得你花时间细读?
我带过不少刚学完Servlet和JSP的学生做课程设计,也帮企业技术负责人筛选过几十套教学型Java Web项目。说实话,市面上90%的“人事系统”源码要么是空壳——只有登录页跳转、增删改查全靠硬编码模拟;要么是过度工程化——Spring Boot+MyBatis+Redis堆砌,新手连pom.xml里哪个依赖干啥都搞不清。而这套“Java Web人事系统源码包”,恰恰卡在最理想的临界点上:它不炫技,但每行代码都有明确目的;它不简化到失真,但又把真实Web开发中绕不开的核心环节——请求生命周期管理、会话状态控制、数据库连接池配置、SQL建表逻辑与业务语义对齐、静态资源组织规范、Tomcat部署路径映射——全都摊开摆在你面前。
关键词里提到的“Java Web”不是泛泛而谈,它特指Servlet 3.1+JSP 2.3这一代标准组合,也就是Java EE 7时代的轻量级Web开发范式;“人事系统”在这里不是功能堆砌,而是以员工(Employee)、部门(Department)、岗位(Position)三张主表为骨架,构建出真实的CRUD闭环:比如新增员工时必须关联有效部门ID,删除部门前需校验是否存在下属员工,这些约束不是写在注释里,而是通过Servlet中的if-else判断、MySQL外键约束、甚至前端JavaScript的实时下拉联动共同实现的。“JSP Servlet”意味着页面渲染与业务逻辑彻底分离——loginForm.jsp只负责收集用户名密码并提交表单,真正的校验、查询、跳转逻辑全在LoginServlet.java里;而“MySQL部署”和“Tomcat运行”则直指落地痛点:hrm.sql里每个字段类型的选择(为什么用VARCHAR(50)而不是TEXT?为什么birth_date用DATE而非VARCHAR?),druid.properties里initialSize=5和maxActive=20的数值依据,甚至WEB-INF/web.xml中 的url-pattern为何要写成/login而不是/login.do——这些都不是随便写的,背后全是生产环境踩坑后沉淀下来的分寸感。
这套代码最适合两类人:一类是刚学完《Java Web编程技术》教材第1~8章的同学,你不需要懂Spring,只要会写for循环、能看懂request.getParameter(),就能从loginForm.jsp开始,顺着表单提交→Servlet接收→数据库查询→跳转响应这条主线,把整个MVC流程走通;另一类是想给新人布置练手项目的讲师或团队技术导师,它没有隐藏陷阱(比如故意漏掉web.xml配置让你报404),也没有冗余模块(没有“新闻管理”“公告板”这类干扰主线的功能),所有代码都在讲同一件事:如何用最基础的技术组件,搭建一个可运行、可调试、可扩展的真实业务系统。我把它比作Java Web世界的“自行车”——没有变速器,但链条、齿轮、刹车都齐全,蹬起来才知道力是怎么传递的。
2. 整体架构与设计思路拆解:为什么选择这套技术组合?
2.1 技术栈选型背后的现实考量
这套系统没用Spring Boot,也没上Vue或React,表面看是“落后”,实则是精准克制。我们来拆解每个组件的选择逻辑:
JSP作为视图层:很多人觉得JSP过时了,但在教学场景下,它的优势无可替代。loginForm.jsp里那句<input type="text" name="username" value="${param.username}" />,能让初学者直观看到EL表达式如何回显表单数据;index.jsp中<c:forEach items="${deptList}" var="dept">配合JSTL标签库,三行代码就完成部门列表渲染——这种“所见即所得”的调试体验,远胜于在Vue里折腾v-model绑定和props传参。更重要的是,JSP的编译机制(.jsp→.java→.class)暴露了Servlet容器如何将页面转换为Java代码,这是理解Web容器底层原理的绝佳入口。
Servlet作为控制器:所有业务逻辑集中在src/com/hrm/servlet/目录下,比如AddEmployeeServlet.java处理新增员工请求。这里刻意回避了Filter或Listener等高级特性,只用doPost()方法完成:获取参数→封装Employee对象→调用DAO层保存→设置请求属性→转发到结果页。这种线性流程强迫开发者思考“请求进来后,每一步该做什么”,而不是被Spring MVC的@RequestMapping自动路由惯坏。我试过让学生先删掉所有@WebServlet注解,手动在web.xml里配置servlet-mapping,他们立刻明白了URL路径与Java类之间的映射关系。
MySQL+Druid作为数据层:hrm.sql建表语句里,employee表的dept_id字段设为NOT NULL且有FOREIGN KEY约束,这直接对应业务规则“员工必须属于某个部门”;而position表的salary_range字段用DECIMAL(10,2)而非FLOAT,是因为薪资计算必须精确到分——这些细节不是DBA拍脑袋定的,而是从业务需求反推的技术决策。Druid连接池取代了原始的DriverManager.getConnection(),不仅因为性能(连接复用减少TCP握手开销),更关键的是它提供了监控能力:druid.properties里配置了filters=stat,启动Tomcat后访问/druid/index.html就能看到SQL执行耗时、慢查询列表,这对理解“为什么数据库操作会成为瓶颈”有具象认知。
Tomcat 9.0作为运行容器:选择9.0而非10.x,是因为它完美兼容Servlet 3.1规范(本项目未使用Servlet 4.0的异步特性),且Windows/Linux/macOS三端安装包体积小、启动快。更重要的是,Tomcat 9的conf/server.xml默认配置足够干净,不会像某些集成环境那样预装乱七八糟的示例应用,学生第一次启动时看到控制台输出“Server startup in [xxx] ms”后直接访问http://localhost:8080/hrm/loginForm.jsp,那种“我亲手跑起来了”的成就感,是任何云服务一键部署都无法替代的。
2.2 目录结构隐含的工程规范意识
资源包里的目录树不是随意排列的,每个节点都对应Java Web开发的强制约定:
-
src/com/hrm/:Java源码根目录,com.hr.m是反向域名命名法(假设公司域名为hrm.com),符合Java包名规范。这里分三层:servlet(控制器)、dao(数据访问)、entity(实体类)、util(工具类),比如DateUtil.java里parseDate()方法专门处理JSP传来的”2023-05-20”字符串转为java.util.Date——这种细粒度拆分,让初学者明白“职责分离”不是空话。 -
WebContent/:Eclipse时代的老派叫法(IDEA里叫webapp),但它承载着Web应用的物理根路径。其中WEB-INF/是安全禁区,外部无法直接访问,所以web.xml、lib目录(存放所有JAR包)、classes目录(编译后的class文件)都放在这里。特别注意WebContent/images/下的logo.png和avatar.jpg,它们被index.jsp用<img src="images/logo.png">引用——这个相对路径的写法,揭示了Tomcat如何将WebContent目录映射为应用根路径(Context Path)。 -
hrm.sql:不是简单CREATE TABLE,而是包含三部分:1)创建数据库CREATE DATABASE IF NOT EXISTS hrm DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;(utf8mb4支持emoji,避免学生将来存微信昵称时报错);2)建表语句中每个字段都加COMMENT,如emp_name VARCHAR(50) NOT NULL COMMENT '员工姓名';3)插入初始化数据,比如预先插入“人力资源部”“技术部”两条记录,确保首次运行时下拉框有选项可选。 -
log4j.properties:配置项log4j.appender.file.File=logs/hrm.log指定了日志文件位置,而log4j.logger.com.hrm.servlet=DEBUG则让所有Servlet类的日志级别设为DEBUG——这意味着LoginServlet.java里写的logger.debug("用户{}尝试登录", username)会在logs/hrm.log里留下痕迹。我建议学生在调试时打开这个开关,亲眼看看一次登录请求触发了多少次日志输出,比任何理论讲解都管用。
3. 核心模块解析与实操要点:从登录到员工管理的完整链路
3.1 登录模块:HTTP协议与会话管理的实战课堂
登录功能看似简单,却是理解Web本质的钥匙。我们从loginForm.jsp开始拆解:
<!-- loginForm.jsp -->
<form action="login" method="post">
<input type="text" name="username" placeholder="用户名"
value="${param.username != null ? param.username : ''}">
<input type="password" name="password" placeholder="密码">
<input type="submit" value="登录">
</form>
这里有两个易忽略的细节:action="login"不是/login.jsp,而是指向Servlet的映射路径;value="${param.username}"利用EL表达式实现表单回显——当用户名错误时,Servlet重定向回此页,用户不用重新输入。对应的LoginServlet.java核心逻辑如下:
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(LoginServlet.class);
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// 1. 基础校验(空值检查)
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
request.setAttribute("errorMsg", "用户名或密码不能为空");
request.getRequestDispatcher("/loginForm.jsp").forward(request, response);
return;
}
// 2. 查询数据库验证
EmployeeDAO dao = new EmployeeDAO();
Employee emp = dao.findByUsername(username);
if (emp == null || !emp.getPassword().equals(password)) {
logger.warn("登录失败:用户名[{}]不存在或密码错误", username);
request.setAttribute("errorMsg", "用户名或密码错误");
request.getRequestDispatcher("/loginForm.jsp").forward(request, response);
return;
}
// 3. 创建会话并存储用户信息
HttpSession session = request.getSession(true);
session.setAttribute("currentUser", emp); // 关键!将用户对象存入session
session.setMaxInactiveInterval(30 * 60); // 30分钟无操作自动失效
// 4. 重定向到主页(避免重复提交)
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
}
这段代码揭示了三个核心概念:
- 请求参数获取:request.getParameter()是Web开发的起点,但要注意它返回的是String,数字类型需手动parseInt();
- 会话管理:request.getSession(true)创建新会话(如果不存在),session.setAttribute()将Employee对象存入内存,后续所有请求都能通过session.getAttribute("currentUser")获取当前登录人——这就是为什么index.jsp能显示“欢迎,张三!”;
- 重定向 vs 转发:最后用response.sendRedirect()而非request.getRequestDispatcher().forward(),因为前者产生302响应让浏览器发起新请求,避免用户刷新时重复提交表单。
提示:初学者常犯的错误是忘记在web.xml中配置servlet-mapping,导致访问/login报404。正确配置如下:
xml <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.hrm.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
3.2 员工管理模块:数据库操作与事务边界的实践
员工管理是系统核心,涉及增删改查全流程。我们以AddEmployeeServlet为例,看如何保证数据一致性:
public class AddEmployeeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取表单参数(注意:前端下拉框name="deptId"传的是部门ID)
String empName = request.getParameter("empName");
String gender = request.getParameter("gender");
String birthDate = request.getParameter("birthDate");
String deptId = request.getParameter("deptId");
// 2. 封装Employee对象(此处省略setter调用)
Employee emp = new Employee();
emp.setEmpName(empName);
emp.setGender(gender.equals("1") ? "男" : "女"); // 前端传1/0,后端转男女
emp.setBirthDate(new SimpleDateFormat("yyyy-MM-dd").parse(birthDate));
emp.setDeptId(Integer.parseInt(deptId));
// 3. 开启事务(关键!)
Connection conn = null;
try {
conn = DruidDataSourceFactory.createDataSource(
new Properties()).getConnection(); // 从Druid获取连接
conn.setAutoCommit(false); // 关闭自动提交
EmployeeDAO dao = new EmployeeDAO(conn); // DAO持有连接
dao.insert(emp);
// 4. 提交事务
conn.commit();
request.setAttribute("msg", "添加成功!");
} catch (Exception e) {
// 5. 回滚事务
if (conn != null) {
try { conn.rollback(); } catch (SQLException ex) {}
}
logger.error("添加员工失败", e);
request.setAttribute("msg", "添加失败:" + e.getMessage());
} finally {
// 6. 关闭连接(Druid会归还到连接池)
if (conn != null) try { conn.close(); } catch (SQLException e) {}
}
request.getRequestDispatcher("/employee/list.jsp").forward(request, response);
}
}
这段代码的教学价值在于:
- 事务控制显式化:conn.setAutoCommit(false)和conn.commit()/conn.rollback()清晰展示了ACID中的原子性。如果插入员工后还要同步更新部门人数统计(假设有dept_employee_count字段),这两步必须在同一个事务里,否则会出现数据不一致。
- 连接池使用规范:DruidDataSourceFactory.createDataSource()读取druid.properties配置,conn.close()不是真正关闭连接,而是归还给Druid连接池——这解释了为什么高并发下数据库连接数不会爆炸式增长。
- 日期格式处理:前端传来的”2023-05-20”字符串需用SimpleDateFormat解析,这里暴露出Java日期API的坑:SimpleDateFormat不是线程安全的,实际项目中应改为局部变量或使用ThreadLocal封装。
注意:hrm.sql中employee表的birth_date字段定义为DATE类型,因此Java中必须用java.sql.Date而非java.util.Date,否则Druid执行INSERT时会报类型转换异常。DAO层insert方法里应这样写:
java PreparedStatement ps = conn.prepareStatement("INSERT INTO employee (...) VALUES (...)"); ps.setDate(4, new java.sql.Date(emp.getBirthDate().getTime())); // 关键转换
3.3 静态资源与前端交互:CSS/JS如何与后端协同工作
index.jsp不仅是展示页面,更是前后端协作的样板:
<!-- index.jsp -->
<link rel="stylesheet" href="css/style.css">
<script src="js/common.js"></script>
<script>
// 页面加载后初始化部门下拉框
window.onload = function() {
loadDepartments(); // 调用common.js中的函数
};
function loadDepartments() {
fetch('department/list') // 发起AJAX请求获取部门列表
.then(response => response.json())
.then(data => {
const select = document.getElementById('deptSelect');
data.forEach(dept => {
const option = document.createElement('option');
option.value = dept.deptId;
option.text = dept.deptName;
select.appendChild(option);
});
});
}
</script>
这里体现了现代Web开发的雏形:
- 资源路径规范:href="css/style.css"中的css/是相对于WebContent的路径,Tomcat会自动映射到WebContent/css/目录;
- AJAX异步加载:fetch('department/list')请求的是DepartmentListServlet,它返回JSON而非HTML,实现了前后端分离的初级形态;
- JavaScript与JSP共存:虽然主体是静态JS,但JSP的EL表达式仍可嵌入,比如<div>欢迎,${sessionScope.currentUser.empName}!</div>——这说明JSP并非完全被取代,而是与JS分工协作。
配套的common.js里有个实用技巧:
// js/common.js
function formatDate(dateStr) {
// 将"2023-05-20"转为"2023年05月20日"
const [y, m, d] = dateStr.split('-');
return `${y}年${m}月${d}日`;
}
这个函数被employee/list.jsp调用,用于美化生日显示。它提醒我们:前端格式化应在客户端完成,避免后端拼接字符串增加耦合。
4. 实操部署与调试全流程:从导入IDE到Tomcat运行
4.1 IDE导入与依赖配置(以IntelliJ IDEA为例)
虽然源码包声称“无需额外下载JAR”,但IDE导入时仍有细节需处理:
- 新建项目:选择“Import Project” → 选择解压后的hrm目录 → 选择“Create project from existing sources”;
- SDK配置:Project SDK选JDK 8(因Tomcat 9要求JDK 8+),Language level保持8;
- 模块识别:IDEA会自动识别src为Sources,WebContent为Resources,但需手动设置:
- 右键WebContent → “Mark Directory as” → “Web Resources Root”;
- File → Project Structure → Modules → Dependencies → 点击“+” → “JARs or directories” → 选择WebContent/WEB-INF/lib/下的所有JAR包(重点确认:servlet-api.jar、jsp-api.jar、druid-1.2.8.jar、mysql-connector-java-8.0.28.jar、log4j-1.2.17.jar); - Artifacts配置(关键步骤):
- File → Project Structure → Artifacts → 点击“+” → “Web Application: Archive” → “For ‘hrm’”;
- Output directory设为out/artifacts/hrm_war_exploded;
- 在左侧Structure树中,展开“Available Elements”,将src编译输出(classes)拖到WEB-INF/classes,将WebContent拖到root节点,将lib下的JAR拖到WEB-INF/lib;
- 最终生成的war包结构必须严格匹配Tomcat要求:/WEB-INF/web.xml、/WEB-INF/classes/、/WEB-INF/lib/。
实操心得:很多学生卡在“找不到javax.servlet.ServletException”,这是因为IDEA未正确关联servlet-api.jar。解决方案:在Dependencies里找到该JAR → 右键 → “Scope” → 设为“Provided”(表示由Tomcat提供,不打包进war)。
4.2 Tomcat 9.0本地部署详解
Tomcat部署不是简单复制war包,需理解路径映射逻辑:
-
Tomcat安装与配置:
- 下载tomcat-9.0.xx.zip,解压到无中文路径目录(如D:\apache-tomcat-9.0.83);
- 修改conf/server.xml:找到<Connector port="8080",可改为port="8081"避免端口冲突;
- 启动bin/startup.bat(Windows)或startup.sh(Linux/macOS),访问http://localhost:8080确认Tomcat首页正常。 -
项目部署方式选择:
- 方式一(推荐教学):exploded目录部署
将IDEA生成的out/artifacts/hrm_war_exploded目录,整体复制到tomcat/webapps/hrm(注意:目录名hrm即应用上下文路径);
启动Tomcat后,自动加载webapps/hrm/WEB-INF/web.xml,生成work/Catalina/localhost/hrm/缓存目录。
- 方式二(生产参考):war包部署
在IDEA中Build → Build Artifacts → Build ‘hrm:war exploded’;
将生成的hrm.war复制到tomcat/webapps/,Tomcat启动时自动解压。 -
启动与验证:
- 启动Tomcat,观察控制台输出:
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [D:\apache-tomcat-9.0.83\webapps\hrm]
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [D:\apache-tomcat-9.0.83\webapps\hrm] has finished in [xxx] ms
- 访问http://localhost:8080/hrm/loginForm.jsp,出现登录页即成功;
- 输入初始账号(查看hrm.sql中INSERT语句,如INSERT INTO employee VALUES (1,'admin','123456',...))。
常见问题排查:若访问报404,按顺序检查:
1)Tomcat是否启动(查看进程或端口占用);
2)webapps/hrm目录是否存在且结构正确(有WEB-INF子目录);
3)WEB-INF/web.xml中servlet-mapping的url-pattern是否与JSP中form action一致;
4)logs/catalina.out是否有ClassNotFoundException(缺少JAR包)或SQLException(数据库连接失败)。
4.3 数据库初始化与连接配置
MySQL部署是独立于Tomcat的环节,需手动执行:
-
创建数据库:
- 启动MySQL服务(如XAMPP控制面板);
- 打开命令行:mysql -u root -p,输入密码后执行:
CREATE DATABASE hrm DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci; -
执行建表脚本:
-mysql -u root -p hrm < hrm.sql(Linux/macOS);
或在MySQL Workbench中打开hrm.sql → 执行;
- 验证:USE hrm; SHOW TABLES;应看到employee、department、position三张表。 -
配置Druid连接参数:
- 编辑WebContent/WEB-INF/classes/druid.properties:
properties driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/hrm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username=root password=你的MySQL密码 initialSize=5 maxActive=20 minIdle=5
- 关键参数说明:
serverTimezone=Asia/Shanghai解决时区问题(否则插入时间可能偏差8小时);
maxActive=20表示连接池最多20个活跃连接,按经验公式:maxActive ≈ 并发用户数 × 每用户平均连接数,教学场景设20足够。
实操技巧:若启动时报
java.sql.SQLException: The server time zone value 'XXX' is unrecognized,说明serverTimezone未配置或配置错误。临时解决方案是在MySQL命令行执行:SET GLOBAL time_zone = '+8:00';,但根本解决需修改druid.properties。
5. 常见问题与排查技巧实录:那些文档里不会写的坑
5.1 编码问题:中文乱码的根源与根治方案
几乎所有新手都会遇到中文乱码,但原因各不相同:
| 场景 | 表现 | 根本原因 | 解决方案 |
|---|---|---|---|
| JSP页面中文显示为?? | loginForm.jsp中“用户名”变成“???” | JSP文件本身编码非UTF-8 | 用记事本打开loginForm.jsp → 另存为 → 编码选UTF-8(无BOM) |
| 表单提交中文变乱码 | 新增员工时姓名存入数据库是“涓??” | Tomcat未配置URIEncoding | 修改conf/server.xml中Connector标签:URIEncoding="UTF-8" |
| MySQL查询中文乱码 | SELECT * FROM employee显示“æŽå” | MySQL连接URL缺characterEncoding | druid.properties中url末尾加&characterEncoding=utf8 |
| 日志文件中文乱码 | logs/hrm.log里打印“????” | log4j.properties未设encoding | 添加log4j.appender.file.Encoding=UTF-8 |
我踩过的坑:某次在Windows上用Notepad++保存JSP,默认编码是ANSI,导致整个项目中文全乱。后来养成习惯:所有文本文件(.jsp/.java/.properties)统一用UTF-8无BOM编码,IDEA中File → Settings → Editor → File Encodings → Global Encoding设为UTF-8。
5.2 会话失效与登录状态丢失
学生常问:“为什么登录后跳转到index.jsp,刷新页面就回到登录页?” 这通常源于:
- Session未正确创建:LoginServlet中
request.getSession(true)被误写为request.getSession(false),后者只获取现有会话,不存在时不创建; - Context Path混淆:前端AJAX请求写成
fetch('/department/list')(绝对路径),但应用部署在/hrm下,实际应为fetch('/hrm/department/list');解决方案是在JSP中用<%=request.getContextPath()%>动态生成路径:fetch('<%=request.getContextPath()%>/department/list'); - 浏览器隐私模式:无痕窗口禁用Cookie,导致JSESSIONID无法存储——提醒学生用普通窗口测试。
5.3 Druid连接池连接超时问题
当频繁操作数据库后报com.alibaba.druid.pool.GetConnectionTimeoutException,说明连接池耗尽。排查步骤:
- 访问
http://localhost:8080/hrm/druid/(Druid监控页面),查看“Active Count”是否长期满额; - 检查DAO层代码:是否每次查询后都调用
conn.close()?遗漏会导致连接泄露; - 调整druid.properties参数:
maxWait=60000(获取连接最大等待60秒);
removeAbandonedOnBorrow=true(借用时移除已废弃连接);
minEvictableIdleTimeMillis=60000(空闲60秒的连接被回收)。
5.4 Eclipse与IDEA的兼容性差异
源码包为Eclipse设计,导入IDEA需额外处理:
- web.xml版本问题:Eclipse默认生成web.xml声明为2.5,而Tomcat 9要求3.1。需手动修改:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"> - JSP编译错误:IDEA需启用JSP支持:Settings → Plugins → 搜索JSP Support → 确保启用;
- 静态资源404:若css/style.css报404,检查Artifacts配置中WebContent是否被正确标记为Web Resources Root。
6. 项目扩展与进阶改造建议:从教学原型到可用系统
这套源码的价值不仅在于“能跑”,更在于它是一块优质的“改造试验田”。以下是几个务实的升级方向,按难度递进:
6.1 安全加固:从明文密码到BCrypt加密
当前密码以明文存储(INSERT INTO employee VALUES (1,'admin','123456',...)),存在严重风险。改造步骤:
- 引入BCrypt依赖(已在lib中);
- 修改Employee实体类,增加passwordHash字段;
- LoginServlet中验证逻辑改为:
if (BCrypt.checkpw(inputPassword, emp.getPasswordHash())) { ... }; - AddEmployeeServlet中插入前计算哈希:
emp.setPasswordHash(BCrypt.hashpw(rawPassword, BCrypt.gensalt()));。
关键点:BCrypt.gensalt(12)中的12是log rounds,值越大越安全但越慢,教学场景用10即可。
6.2 分页功能:解决大数据量下的性能瓶颈
当前员工列表直接SELECT * FROM employee,数据超百条就会卡顿。引入PageHelper:
- 在pom.xml中添加依赖(若用Maven)或复制pagehelper-5.3.0.jar到lib;
- 修改EmployeeDAO.list()方法:
PageHelper.startPage(pageNum, pageSize);
List<Employee> list = mapper.selectByExample(example);
PageInfo<Employee> pageInfo = new PageInfo<>(list);; - JSP中用
<c:forEach items="${pageInfo.list}" var="emp">遍历,并添加分页导航。
6.3 前端现代化:用Bootstrap重构界面
保留JSP后端逻辑,仅替换前端样式:
- 下载Bootstrap 5 CSS/JS,放入WebContent/css/bootstrap.min.css;
- index.jsp中引入:
<link href="css/bootstrap.min.css" rel="stylesheet">; - 将原生table替换为Bootstrap table:
<table class="table table-striped">
<thead><tr><th>姓名</th><th>部门</th></tr></thead>。
此举能让界面瞬间专业,且不影响后端任何代码。
6.4 部署自动化:从手动复制到Maven打包
当前依赖手动管理,改为Maven可提升工程规范性:
- 创建pom.xml,声明依赖:
xml <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!-- 其他依赖 --> </dependencies> - 配置maven-war-plugin,指定webResources目录;
- 执行
mvn clean package生成war包,比IDE导出更可控。
个人体会:我最初教学生时坚持用纯手工配置,直到有学生因jar包版本冲突调试三天。后来改为“先手工理解,再Maven自动化”,效果显著——他们既懂原理,又掌握现代工程实践。
这套人事系统源码,就像一把解剖刀,把Java Web开发的肌肉、血管、神经一层层剥开给你看。它不承诺“学会就能年薪30万”,但只要你跟着loginForm.jsp里的form action,一路追踪到EmployeeDAO的executeUpdate(),搞懂每一次request.getAttribute()和session.setAttribute()的来龙去脉,你就真正跨过了从语法学习到工程实践的那道门槛。我见过太多学生,在反复修改web.xml的url-pattern、调试Druid连接池超时、修复MySQL时区报错的过程中,突然某天说:“原来HTTP请求真的是一条流水线啊。”——那一刻,代码不再是字符,而是活的系统。
简介:这个人事管理Web项目用纯Java Web技术实现,前端用JSP渲染登录页loginForm.jsp和主页index.jsp,配合CSS样式、JavaScript交互脚本和图片资源;后端由Servlet处理员工、部门、岗位等业务逻辑,数据存入MySQL,通过Druid连接池高效访问数据库。项目自带hrm.sql建表语句,定义了完整的员工信息结构;log4j.properties支持日志记录,druid.properties配置数据库连接参数;src目录下是com包结构的Java源码,编译后class文件放在classes目录;WebContent包含标准WEB-INF结构,兼容Tomcat 9.0;所有依赖JAR已打包在内,导入Eclipse或IDEA后无需额外下载库即可运行调试。部署时只需将项目发布到Tomcat,启动服务就能访问登录界面,适合Java初学者练手和教学演示。

469

被折叠的 条评论
为什么被折叠?



