news 2026/9/15 2:53:34

基于JSP的企业人事管理系统:源码实现与项目部署全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于JSP的企业人事管理系统:源码实现与项目部署全解析

简介:基于JSP的企业人事管理系统毕业设计资源包,为Java Web方向的毕设学生与初学者提供完整实战范本。系统覆盖用户管理、人事档案、考勤、薪酬、绩效、培训及报表等核心模块,从功能需求到数据库设计均有代码与文档支撑。压缩包共277个文件,主体为104个JSP页面和14个Java源文件,辅以CSS、JavaScript、XML配置及class文件,并含项目报告doc文档与数据库mdf文件,整套容量仅3.18MB,轻量但结构完整。已有51人学习下载,适合用来对照理解Servlet+JSP的MVC分层、会话管理、权限控制及MySQL表关系设计等关键知识点。通过阅读项目报告与运行源码,可快速掌握从环境搭建到功能实现的完整链路,是毕业设计选题或Spring Boot过渡前打基础的实用参考资料。

1. 基于 JSP 的企业人事管理系统:毕业设计最常见的分层切入点

一个基于 JSP 的企业人事管理系统,是毕业设计里被选了很多年但仍然值得认真做的题目。很多人觉得 JSP 已经是老技术,但正是因为它老,Servlet 生命周期、请求转发、JDBC 事务边界、Filter 拦截这些概念都被完整保留着,答辩老师一问就能展开;而且人事系统的业务边界明确,员工、部门、考勤、登录权限都能用清晰的表和字段表达,不需要引入复杂概念。标题里的源代码和项目报告,意味着交付物不是“能跑就行”,而是必须有一份能解释设计过程的文档。这套系统要解决的是企业内部“统一管档案、管考勤、管权限”的后台需求。从技术上,用 JSP 展示页面、Servlet 处理请求、JDBC 访问数据库,三个环节串成一条完整的数据链路,正好对应软件工程里的表现层、业务层、数据访问层。适合计算机专业做毕业设计或课程设计的在校生,也适合想快速搭一个纯 Java Web 后台原型、又不想引入整套 Spring 生态的小团队。后面的内容会沿着技术选型、数据库建模、关键编码、Tomcat 部署四条线展开。

2. 企业人事管理系统的 JSP 技术选型与数据库设计

这套系统推荐使用 JSP + Servlet + JDBC 的三层结构,而不是在 JSP 里写 Java 小脚本,也不是一上来就套 Spring Boot。原因是标题点名了 JSP,同时毕业设计与实现考察的核心是数据库设计和编程基础。既然使用 JDBC,就必须把连接参数、SQL、结果集映射分开写,后续每一个业务模块才能有统一的数据访问方式。JSP 负责渲染页面,Servlet 负责接收请求和跳转,DAO 负责所有 SQL 操作,职责边界清楚以后,项目报告里的架构图也容易画。

2.1 选型:JSP + Servlet + JDBC 的适用边界和实现原则

JSP + Servlet + JDBC 的适用边界是业务量不大、没有复杂缓存和分布式需求。人事管理系统的主要操作是单表或两表以上的增删改查、权限控制、简单统计,关系型数据库足够容纳。Tomcat 自带的 Servlet 容器能直接运行 JSP 和编译后的类文件,不需要额外引入 Spring 上下文,部署体积小,这对毕业设计现场演示更友好。需要明确的是,这里的 JSP 并不是指页面里直接写 Java,而是用 JSP 负责展示,Servlet 负责控制,JavaBean 承载数据。实现原则有两条:第一,不在 JSP 里直接写数据库连接代码;第二,不在 Servlet 里写大段业务逻辑,只做参数收集、调用 DAO、转发或重定向。这样答辩时才能说清楚表现层与控制层分离。

2.2 数据库建模:部门、员工、考勤、用户四张表的关系

人事管理系统的核心实体可以收敛为四张表:部门表、员工表、考勤表、用户表。部门与员工是一对多,员工与考勤是一对多,用户表负责登录账号与角色。如果想做得更完整,可以加薪资表、招聘表,但作为毕业设计,四张表已经能覆盖“大而全”的功能列表,并且数据关系清晰,ER 图画起来也干净。字段类型上要注意:员工编号用 varchar 而不是 int,因为编号经常带前导零;工资用 decimal(10,2) 而不是 float;所有表都加 create_time 和 update_time,项目报告里可以写成通用审计字段。

表名核心字段关联关系用途
departmentid, dept_name, manager_id与 employee 一对多部门层级
employeeid, emp_no, emp_name, dept_id, hire_date, salary从属于部门员工档案
attendanceid, emp_id, work_date, status, overtime_hours属于员工考勤记录
sys_userid, username, password, role, emp_id对应员工档案登录权限

建表 SQL 中,先删除从表再删除主表,避免外键约束报错。department 的 manager_id 先不建自引用外键,这样删除部门时不会出现级联联动过于复杂的问题;sys_user 的 password 字段长度给 64,是为了容纳 MD5 或 SHA-256 摘要,而不是明文。status 字段用 TINYINT 存 0、1、2,分别表示未打卡、正常、异常,比直接存文字更省空间,也方便后台做统计。

DROP TABLE IF EXISTS attendance; DROP TABLE IF EXISTS employee; DROP TABLE IF EXISTS department; DROP TABLE IF EXISTS sys_user; CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL UNIQUE, manager_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(20) NOT NULL UNIQUE, emp_name VARCHAR(50) NOT NULL, gender CHAR(2), dept_id INT, hire_date DATE, salary DECIMAL(10,2), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES department(id) ); CREATE TABLE attendance ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT, work_date DATE, status TINYINT DEFAULT 0, overtime_hours DECIMAL(4,1), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_att_emp FOREIGN KEY (emp_id) REFERENCES employee(id) ); CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(20) DEFAULT 'USER', emp_id INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );

上面的建表顺序是先删子表再删主表,SQL 脚本才能重复执行。emp_no 加了 UNIQUE 约束,是用来校验员工编号不能重复;dept_id 上的外键保证员工不能挂到一个不存在的部门。attendance 的 work_date 和 emp_id 组合起来应该加 UNIQUE 约束,表示一个员工一天只能有一条考勤记录,这个约束可以在项目报告里写成“业务规则落到数据库层面”。

2.3 JDBC 连接参数的存放和 DAO 的基础写法

一个常见的错误做法是把数据库地址、账号密码直接散落在每个类里,遇到环境变化要改十几个文件。可交付的项目里应该有一个 db.properties 文件集中管理参数,再用一个静态代码块加载。这样既能解释自己考虑了配置与代码分离,也方便对方打开源码后,只改一个文件就能跑起来。

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/hr_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456
// DBUtil.java import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class DBUtil { private static String url; private static String username; private static String password; static { try (InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("db.properties")) { Properties props = new Properties(); props.load(in); url = props.getProperty("jdbc.url"); username = props.getProperty("jdbc.username"); password = props.getProperty("jdbc.password"); Class.forName(props.getProperty("jdbc.driver")); } catch (Exception e) { throw new ExceptionInInitializerError("数据库配置加载失败"); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, username, password); } }

逻辑说明:db.properties 放在 classes 根目录下,ClassLoader 的 getResourceAsStream 会从 classpath 读取。url 里的 characterEncoding=utf8 与 useUnicode=true 配套,解决中文写入乱码;serverTimezone 指定为 Asia/Shanghai,避免 MySQL 8 的时区警告。Class.forName 在 JDBC 4.0 之后可以省略,但保留对旧版本驱动兼容性更好。

JDBC 连接每次调用都新建,并发量不高时没问题;如果以后想优化,可以换 Druid 连接池,但毕业设计里直接使用 DriverManager 更容易把原理讲清楚。相比连接池,更重要的是连接不泄漏。

2.3.1 资源释放的 finally 模板

用 JDBC 时,Connection、PreparedStatement、ResultSet 三个对象都需要释放。关闭顺序与创建顺序相反:先关 ResultSet,再关 PreparedStatement,最后关 Connection。JDK 7 之后推荐用 try-with-resources,代码更短且不容易漏。

// EmployeeDAO 中的查询片段 String sql = "SELECT * FROM employee WHERE id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, employeeId); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { // 映射成 Employee 对象 } } } catch (Exception e) { e.printStackTrace(); }

try-with-resources 会自动关闭括号里声明的资源,不需要在 finally 里手动调用 close。注意参数占位符的下标从 1 开始,ps.setInt(1, employeeId) 表示把 employeeId 放进第一条 SQL 参数的问号位置。这种写法在项目报告里可以写成“使用自动资源管理避免连接泄漏”,比贴一大段 finally 代码更容易解释。

3. 用 JSP + Servlet + JDBC 把员工模块从登录落到列表

登录、员工列表、新增员工是人事管理系统最核心的三段链路,这三块能跑通,部门管理和考勤记录基本就是同一种写法的重复。这一章从权限过滤说起,落到如何查询并渲染员工列表,再到新增员工时最容易翻车的两个点。

3.1 登录权限控制:用 Filter 拦截未登录请求还是每个页面单独判断

写 JSP 人事系统时,最不专业的现象是用户不登录也能直接访问员工列表页。常见做法不是在每个 JSP 页面里判断 session,而是用一个 Filter 拦截所有/pages/*请求。Filter 在 Servlet 容器处理请求之前执行,只需要把未登录用户拦回 login.jsp,这样登录页之外的受限页面就都有了统一控制。如果以后要把角色权限细分,比如普通用户不能访问系统设置页,也只需要在这个 Filter 里扩展判断。

// AuthFilter.java import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebFilter("/pages/*") public class AuthFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); if (session == null || session.getAttribute("loginUser") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); } }

逻辑说明:@WebFilter("/pages/*")是 Servlet 3.0 的注解方式,web.xml 里不用额外配置映射。getSession(false)表示当前没有 session 时不主动创建,直接返回 null,避免每个静态资源访问都生成新 session。sendRedirect拼接的是request.getContextPath(),这样项目部署在带应用路径的 Tomcat 下也能正确跳转。注意 Filter 不拦截 login.jsp 本身,所以登录页要放在/login.jsp,而所有受保护页面统一放到/pages/目录下,这是 JSP 后台项目常用的路径约定。

3.2 员工列表:Servlet 查数据、JSP 用 JSTL 渲染的完整链路

点击“员工管理”菜单后,浏览器请求/employee/list.do。Servlet 的 doGet 方法调用 EmployeeDAO 的 findAll 方法,把 List 放到 request 作用域,再用request.getRequestDispatcher("/pages/employee_list.jsp").forward()转发。JSP 页面通过 EL 表达式遍历 List,JSTL 标签负责循环和格式化。

// EmployeeListServlet.java @WebServlet("/employee/list.do") public class EmployeeListServlet extends HttpServlet { private EmployeeDAO employeeDao = new EmployeeDAO(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { List<Employee> list = employeeDao.findAll(); req.setAttribute("employeeList", list); req.getRequestDispatcher("/pages/employee_list.jsp").forward(req, resp); } catch (Exception e) { req.setAttribute("errorMsg", "查询员工列表失败"); req.getRequestDispatcher("/pages/error.jsp").forward(req, resp); } } }

参数说明:request.setAttribute里的 keyemployeeList必须与 JSP 的 EL 变量名一致;forward是服务端内部转发,浏览器地址栏不会变化,所以刷新页面会重复请求同一个 Servlet。这里不能改用sendRedirect,因为重定向是二次请求,request 作用域里的数据已经丢失。如果确实要重定向,只能把 id 等参数拼到 URL 上再查一次。

JSP 页面的循环渲染代码如下:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <table class="table"> <tr> <th>员工编号</th><th>姓名</th><th>所属部门</th><th>入职日期</th> </tr> <c:forEach items="${employeeList}" var="emp"> <tr> <td>${emp.empNo}</td> <td>${emp.empName}</td> <td>${emp.deptName}</td> <td>${emp.hireDate}</td> </tr> </c:forEach> </table>

逻辑说明:JSTL 标签库引入的 uri 在 Tomcat 9 里仍然使用http://java.sun.com/jsp/jstl/core,不要因为网上有人提到 jakarta 前缀就乱改,否则会报 TLD 加载错误。${emp.empName}会调用 Employee 类的 getEmpName 方法,所以实体类属性命名必须规范。这里的deptName不是 employee 表的直接字段,而是 DAO 中联表查询得到的虚拟属性。

数据展示方式代码位置适用场景
JSP Scriptlet页面内写 Java简单演示,不推荐
EL + JSTL页面标签本项目推荐,页面干净
前端 JS 渲染 JSON浏览器端适合前后端分离,偏题

列表中,EL + JSTL 是 JSP 项目里维护成本最低的方案。如果页面出现null字符串,先检查 getter 方法返回的字段名是否正确;如果出现空列表,先直接访问一次 Servlet 地址确认 EmployeeDAO 是否查到数据,不要急着改页面。

3.3 新增员工时最常踩的两个坑:中文乱码和日期格式

表单提交到/employee/add.do,doPost 里要做的第一件事是设置request.setCharacterEncoding("UTF-8")。这个设置必须放在读取任何request.getParameter之前,否则 POST 请求的中文参数会变成问号。GET 请求的乱码要到 Tomcat 的 server.xml 中给 Connector 配置 URIEncoding,而 POST 请求可以用一个过滤器统一处理。

3.3.1 用一个字符编码过滤器统一处理请求与响应编码
// EncodingFilter.java @WebFilter("/*") public class EncodingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } }

逻辑说明:这个过滤器拦截所有路径,比在 Servlet 里逐个设置更省事。response.setCharacterEncoding 也顺手设置,避免响应输出 ISO-8859-1。如果 JSP 页面中文正常,但提交表单后数据库里乱码,要检查三处:JSP 页面的 pageEncoding、MySQL 表字符集、JDBC url 里的 characterEncoding,三者必须一致。还有一个细节:这个 Filter 放在 AuthFilter 之前还是之后会影响执行顺序,编码过滤应该在最外层先执行。

3.3.2 日期字符串与 java.sql.Date 的转换

HTML 表单里的 input type="date" 提交格式是 yyyy-MM-dd,后端拿到的 String 需要转成 java.sql.Date 才能写入数据库。

String hireDate = req.getParameter("hireDate"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); java.util.Date parsedDate = sdf.parse(hireDate); java.sql.Date sqlDate = new java.sql.Date(parsedDate.getTime()); ps.setDate(4, sqlDate);

逻辑说明:SimpleDateFormat 的 pattern 必须与表单格式一致,parse 后得到的是 java.util.Date,不能直接传给 PreparedStatement 的 setDate 方法,需要先转换成 java.sql.Date。如果用户没有填写日期,getParameter 拿到的是空字符串,parse 会抛 ParseException,所以在 Servlet 里要 catch 住异常并把错误信息回传到表单页,而不是让异常直接打到 Tomcat 错误页。处理新增员工时,建议先获得 empNo 是否存在重复,再执行 insert,数据库唯一约束负责兜底,这样能让用户看到业务提示而不是底层异常。

4. 企业人事管理系统部署到 Tomcat 时的目录、报错与运行验证

代码写完不是终点,要能在干净 Tomcat 环境下运行,这套 JSP 系统才算真正交付。部署阶段最常见的问题集中在目录结构缺文件、依赖 jar 包没放进 lib、JSP 编译缓冲未清理这三类。下面这些检查项,每一步都能能对应到项目报告里的“运行环境与部署说明”。

4.1 可交付的源代码压缩包建议采用这样的目录结构

一个可交付的 JSP 项目压缩包,不应只是 IDE 工作区,还要让对方能直接提到 Tomcat 里跑。Eclipse 项目里,WebContent 目录是部署根目录;Maven 项目则对应 src/main/webapp。压缩包根部最好放一个说明文件,写清 JDK、Tomcat、MySQL 版本以及初始化数据库的顺序。

hr_system/ ├── db/ │ └── init.sql ├── src/ │ ├── com/hrsys/dao/ │ ├── com/hrsys/entity/ │ ├── com/hrsys/filter/ │ └── com/hrsys/servlet/ ├── WebContent/ │ ├── WEB-INF/ │ │ ├── lib/ │ │ └── web.xml │ ├── css/ │ ├── js/ │ ├── login.jsp │ └── pages/ │ ├── employee_list.jsp │ └── department_list.jsp └── 项目报告/ ├── 需求分析.docx ├── 数据库设计.docx └── 答辩演示.md

db/ 目录里放 init.sql,项目报告里的数据库截图要和这里的 SQL 对应。WEB-INF/lib 需要放置 mysql-connector-java 和 jstl 两个关键 jar 包;没有 jstl 会导致 JSP 页面停止渲染并报缺少标签库。lib 下的 jar 包文件名不要出现空格和中文,否则 Tomcat 在解压时可能识别不到。

4.2 在 Tomcat 上部署 JSP 项目的最小操作步骤

在 Tomcat 上部署 JSP 项目有两种方式:一种是直接用 IDE 导出的 war 包复制到 webapps 目录,另一种是把 WebContent 整个复制成一个文件夹。对毕业设计演示场景,war 包更合适,因为迁移到新机器时不会遗漏单个文件。打包可以借助 IDE 的 export 功能,也可以直接在命令行执行 jar 命令。

cd hr_system jar cvf hr.war -C WebContent . cp hr.war /opt/tomcat/webapps/ cd /opt/tomcat/bin ./startup.sh tail -f /opt/tomcat/logs/catalina.out

逻辑说明:jar cvf hr.war -C WebContent .表示进入 WebContent 目录并把当前目录内容打包为 hr.war,这样解压后的应用根路径就是登录页。Tomcat 启动时检测到 webapps 目录下的 war 包会自动解包。看到日志输出Deployment of web application archive hr.war has finished即表示部署成功;如果端口被占用或数据库没启动,catalina.out 最后会给出异常堆栈。

如果不想每次启动都手动设环境变量,也可以在 catalina.sh 中加上:

JAVA_OPTS="-Xms256m -Xmx512m -Dfile.encoding=UTF-8"

参数说明:JVM 堆内存给 256MB 到 512MB,对一个演示用的小型人事系统已经足够。-Dfile.encoding=UTF-8用来防止 Windows 环境下控制台日志输出乱码。修改端口时,Tomcat 的 conf/server.xml 中把 Connector 的 port 从 8080 改成 8088,访问地址就变成http://localhost:8088/hr/pages/index.jsp。应用上下文路径是 hr,因为 war 包名是 hr.war。

4.3 启动后遇到 404、500、中文乱码时先查哪些位置

404 的第一反应是看浏览器地址栏有没有包含应用路径/hr/,因为 webapps 下解压出的目录名是 hr,URL 没带应用路径就会打到 Tomcat 根应用。500 分为两类:如果错误行指向 JSP 中某个属性不存在,多半是实体类 getter 方法名写错;如果报org.apache.jasper.JasperException且堆栈提到 JSTL,先确认 WEB-INF/lib 里有没有 jstl 和 standard 包,并将 jstl 换成 1.2 版本。中文乱码则优先看 JSP 页面第一行的 pageEncoding,再看 db.properties 中的字符集参数。

现象优先怀疑位置排错入口
404应用上下文路径、文件防止位置localhost.log
500Java 异常、JSP 编译异常、jar 包缺失catalina.out
中文乱码JSP pageEncoding、请求字符集、MySQL 表字符集浏览器响应头与数据库日志
4.3.1 JSP 编译后的 class 文件放在哪里

经常有人问“jsp编译class文件保存在哪里”,这个问题通常出现在修改 JSP 后页面不生效的场景。Tomcat 默认把 JSP 翻译后的 .java 和 .class 放到工作目录:Linux 下位于/opt/tomcat/work/Catalina/localhost/hr/org/apache/jsp/,Windows 下位于%CATALINA_BASE%\work\Catalina\localhost\项目名\org\apache\jsp。如果修改 JSP 后页面没有变化,先删掉 work 目录下对应项目目录,再重启 Tomcat,强制触发重新编译。

find /opt/tomcat/work -name "*employee_list*.java"

这段命令用来快速定位编译后的 JSP 类。注意,编译后的 .java 文件里大量使用 out.write 拼接 HTML,不适合直接阅读业务逻辑,它的主要价值是确认页面是否进入容器编译流程。JSP 的新代码没有被编译,通常就是这个 work 缓存目录在作怪。

5. 给 JSP 人事管理系统源码加一层能写进项目报告里的请求计时

项目报告里最容易被追问的是“系统有多大工作量”和“哪个地方体现你的工程意识”。与其泛泛说“系统性能良好”,不如在源码里加一个最轻量的请求计时过滤器,把每次请求耗时打印到日志。这个技巧代码量很小,但能证明你考虑到了可观测性,答辩现场也有实际输出可展示。

5.1 用 Filter 给每个请求计时,并把日志输出到控制台

// AccessLogFilter.java @WebFilter("/*") public class AccessLogFilter implements Filter { private static final Logger LOG = Logger.getLogger(AccessLogFilter.class.getName()); @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { long start = System.nanoTime(); chain.doFilter(req, resp); long cost = (System.nanoTime() - start) / 1_000_000; String path = ((HttpServletRequest) req).getRequestURI(); LOG.info(path + " - " + cost + " ms"); } }

逻辑说明:System.nanoTime 适合测量短时间间隔,不能用 System.currentTimeMillis 代替。chain.doFilter 执行完后计算耗时,再把请求路径和毫秒数写入日志。Filter 的执行顺序配置在 web.xml 或注解中,通常编码过滤在最前面,AccessLogFilter 放在业务 Filter 后面,这样计时的才包含真实处理过程。

如果把这段代码放进系统的 Tomcat 里,访问登录页一次,日志里会打印类似这样的内容:

INFO [AccessLogFilter] /hr/login.jsp - 3 ms INFO [AccessLogFilter] /hr/pages/employee_list.jsp - 15 ms

这两个数字会随机器配置和数据库状态变化,但是在项目报告里附上日志截图,并写清测试环境为 Windows 11、Tomcat 9、MySQL 8,就能作为“系统运行效率验证”的佐证。如果想让日志更完整,还可以从 session 中取出当前登录用户的 username 拼进日志:

String username = "anonymous"; HttpSession session = ((HttpServletRequest) req).getSession(false); if (session != null && session.getAttribute("loginUser") != null) { username = (String) session.getAttribute("loginUser"); } LOG.info(username + " - " + path + " - " + cost + " ms");

注意,getSession(false) 在这里不会创建新 session,避免了所有静态资源请求都触发 session 创建。日志里同时保留用户名和请求耗时,答辩时就能向老师说明:源码里记录了操作痕迹,后续可以配合 Filter 做简单的行为审计。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 2:53:31

算法复杂度实战指南:从TLE到AC的必备分析技巧

最近带学弟学妹备赛的时候&#xff0c;发现一个特别普遍的现象&#xff1a;板子背得滚瓜烂熟&#xff0c;线段树、KMP张口就来&#xff0c;可是一提交就是一片红&#xff0c;不是TLE&#xff08;Time Limit Exceeded&#xff09;就是MLE&#xff08;Memory Limit Exceeded&…

作者头像 李华
网站建设 2026/9/15 2:52:01

Keysight HD304MSO高清混合信号示波器深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 2:51:16

ArmorPaint PBR纹理直绘原理与Git工作流实践

1. ArmorPaint 是什么&#xff1a;不是 Photoshop 的 3D 纹理画布&#xff0c;而是专为实时 PBR 工作流设计的开源工具 ArmorPaint 这个名字乍一听容易让人联想到“装甲涂装”或者某种军事建模软件&#xff0c;但其实它和坦克、战机毫无关系——它是一把真正为 3D 艺术家打磨了…

作者头像 李华
网站建设 2026/9/15 2:51:02

Python代码格式化工具Black的核心特性与应用指南

1. 为什么Python开发者需要Black作为一名长期与Python打交道的开发者&#xff0c;我深刻体会到代码风格一致性对团队协作的重要性。Black的出现彻底改变了我们处理代码格式的方式——它不再是一个可选项&#xff0c;而成为了现代Python开发的标配工具。Black最核心的价值在于它…

作者头像 李华