JavaWeb(SpringBoot3+vue3)教学管理系统项目实战之获取当前登录员工ThreadLocal
- ThreadLocal并不是一个Thread,而是Thread的局部变量。
- ThreadLocal为每个线程提供一份单独的存储空间,具有线程隔离的效果,不同的线程之间不会相互干扰。
- 常见方法:
- public void set(T value) 设置当前线程的线程局部变量的值
- public T get() 返回当前线程所对应的线程局部变量的值
- public void remove() 移除当前线程的线程局部变量
具体操作步骤:
- 定义ThreadLocal操作的工具类,用于操作当前登录员工ID。
在 com.zidiu.utils 引入工具类 CurrentHolder
- package com.zidiu.utils;
-
- public class CurrentHolder {
-
- private static final ThreadLocal<Integer> CURRENT_LOCAL = new ThreadLocal<>();
-
- public static void setCurrentId(Integer employeeId) {
- CURRENT_LOCAL.set(employeeId);
- }
-
- public static Integer getCurrentId() {
- return CURRENT_LOCAL.get();
- }
-
- public static void remove() {
- CURRENT_LOCAL.remove();
- }
- }
复制代码
- 在TokenFilter中,解析完当前登录员工ID,将其存入ThreadLocal(用完之后需将其删除)。
- package com.zidiu.filter;
-
- import com.zidiu.utils.CurrentHolder;
- import com.zidiu.utils.JwtUtils;
- import io.jsonwebtoken.Claims;
- import jakarta.servlet.*;
- import jakarta.servlet.annotation.WebFilter;
- import jakarta.servlet.http.HttpServletRequest;
- import jakarta.servlet.http.HttpServletResponse;
- import lombok.extern.slf4j.Slf4j;
- import java.io.IOException;
-
- @Slf4j
- @WebFilter(urlPatterns = "/*")
- public class TokenFilter implements Filter {
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) servletRequest;
- HttpServletResponse response = (HttpServletResponse) servletResponse;
-
- //1. 获取请求的url地址
- String uri = request.getRequestURI(); // /employee/login
- //String url = request.getRequestURL().toString(); // http://localhost:8080/employee/login
-
- //2. 判断是否是登录请求, 如果url地址中包含 login, 则说明是登录请求, 放行
- if (uri.contains("login")) {
- log.info("登录请求, 放行");
- filterChain.doFilter(request, response);
- return;
- }
-
- //3. 获取请求中的token
- String token = request.getHeader("token");
-
- //4. 判断token是否为空, 如果为空, 响应401状态码
- if (token == null || token.isEmpty()) {
- log.info("token为空, 响应401状态码");
- response.setStatus(401); // 响应401状态码
- return;
- }
-
- //5. 如果token不为空, 调用JWtUtils工具类的方法解析token, 如果解析失败, 响应401状态码
- try {
- Claims claims = JwtUtils.parseJWT(token);
- Integer empId = Integer.valueOf(claims.get("id").toString());
- CurrentHolder.setCurrentId(empId);
- log.info("token解析成功, 放行");
- } catch (Exception e) {
- log.info("token解析失败, 响应401状态码");
- response.setStatus(401);
- return;
- }
-
- //6. 放行
- filterChain.doFilter(request, response);
-
- //7. 清空当前线程绑定的id
- CurrentHolder.remove();
- }
- }
复制代码
- 在AOP程序中,从ThreadLocal中获取当前登录员工的ID。
- package com.zidiu.aop;
-
- import com.zidiu.anno.LogOperation;
- import com.zidiu.mapper.OperateLogMapper;
- import com.zidiu.pojo.OperateLog;
- import com.zidiu.utils.CurrentHolder;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- import java.time.LocalDateTime;
- import java.util.Arrays;
-
- @Aspect
- @Component
- public class OperationLogAspect {
-
- @Autowired
- private OperateLogMapper operateLogMapper;
-
- // 环绕通知
- @Around("@annotation(log)")
- public Object around(ProceedingJoinPoint joinPoint, LogOperation log) throws Throwable {
- // 记录开始时间
- long startTime = System.currentTimeMillis();
- // 执行方法
- Object result = joinPoint.proceed();
- // 当前时间
- long endTime = System.currentTimeMillis();
- // 耗时
- long costTime = endTime - startTime;
-
- // 构建日志对象
- OperateLog operateLog = new OperateLog();
- operateLog.setOperateEmpId(getCurrentUserId()); // 需要实现 getCurrentUserId 方法
- operateLog.setOperateTime(LocalDateTime.now());
- operateLog.setClassName(joinPoint.getTarget().getClass().getName());
- operateLog.setMethodName(joinPoint.getSignature().getName());
- operateLog.setMethodParams(Arrays.toString(joinPoint.getArgs()));
- operateLog.setReturnValue(result.toString());
- operateLog.setCostTime(costTime);
-
- // 插入日志
- operateLogMapper.insert(operateLog);
- return result;
- }
-
- // 示例方法,获取当前用户ID
- private int getCurrentUserId() {
- return CurrentHolder.getCurrentId();
- }
- }
复制代码
在同一个线程/同一个请求中,进行数据共享就可以使用 ThreadLocal。
SpringBoot3+Vue3开发综合实战项目:
JavaWeb(SpringBoot3+vue3)开发+教学管理系统项目实战
|