基于threadlocal实现某个模块下的上下文传递(这里是传递的是用户id)

目的是为了 建一个工具类,让每个地方都可以用LongUtil去获取登录用户的id

 在subject应用层的全局配置GlobalConfig中实现以下的方法 :

/**
 * 添加拦截器到Spring MVC的拦截器注册表中。
 * 
 * 本方法重写了Spring Boot默认的addInterceptors方法,目的是为了注册自定义的拦截器LoginInterceptor。
 * 通过这个拦截器,可以对所有请求进行预处理,例如检查用户是否已登录。
 * 
 * @param registry 拦截器注册表,用于添加新的拦截器和配置其对应的请求路径。
 */
@Override
protected void addInterceptors(InterceptorRegistry registry) {
    // 添加LoginInterceptor拦截器,并配置其拦截所有请求的路径。
    registry.addInterceptor(new LoginInterceptor())
            .addPathPatterns("/**");
}

LoginInterceptor拦截器:

/**
 * 登录拦截器类,用于在请求处理之前和处理之后进行相关逻辑处理。
 * 实现了HandlerInterceptor接口,以拦截器的形式增强应用程序的功能。
 */
/**
 * 登录拦截器
 *
 * @author: ChickenWing
 * @date: 2023/11/26
 */
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 在请求处理之前执行的逻辑。
     * 从请求头中获取登录ID,并将其设置到登录上下文中,以便后续使用。
     *
     * @param request  HttpServletRequest对象,代表客户端的请求。
     * @param response HttpServletResponse对象,用于向客户端发送响应。
     * @param handler  将要处理请求的目标对象,可以是Controller等。
     * @return boolean 返回值总是true,表示继续处理请求。
     * @throws Exception 如果在处理过程中发生异常,可以抛出。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取登录ID
        String loginId = request.getHeader("loginId");
        // 将登录ID设置到登录上下文中
        LoginContextHolder.set("loginId", loginId);
        // 允许请求继续处理
        return true;
    }

    /**
     * 在请求处理完成后执行的逻辑。
     * 从登录上下文中移除登录ID,进行资源清理。
     *
     * @param request  HttpServletRequest对象,代表客户端的请求。
     * @param response HttpServletResponse对象,用于向客户端发送响应。
     * @param handler  将要处理请求的目标对象,可以是Controller等。
     * @param ex       在处理过程中抛出的异常,如果有的话。
     * @throws Exception 如果在处理过程中发生异常,可以抛出。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        // 从登录上下文中移除登录ID
        LoginContextHolder.remove();
    }

}

LoginContextHolder:
 

// 登录上下文持有器类,用于在多线程环境中存储和获取登录信息
public class LoginContextHolder {

    // 静态线程局部变量,使用InheritableThreadLocal保证子线程可以访问父线程的数据
    private static final InheritableThreadLocal<Map<String, Object>> THREAD_LOCAL 
            = new InheritableThreadLocal<>();

    // 设置线程局部变量的键值对
    public static void set(String key, Object val) {
        // 获取或创建线程局部变量映射
        Map<String, Object> map = getThreadLocalMap();
        // 将键值对存入映射
        map.put(key, val);
    }

    // 根据键获取线程局部变量的值
    public static Object get(String key){
        // 获取线程局部变量映射
        Map<String, Object> threadLocalMap = getThreadLocalMap();
        // 返回对应键的值
        return threadLocalMap.get(key);
    }

    // 专门用于获取登录ID
    public static String getLoginId(){
        // 从线程局部变量映射中获取键为"loginId"的值并转换为String类型
        return (String) getThreadLocalMap().get("loginId");
    }

    // 清除线程局部变量
    public static void remove(){
        // 移除线程局部变量
        THREAD_LOCAL.remove();
    }

    // 获取或创建线程局部变量映射
    public static Map<String, Object> getThreadLocalMap() {
        // 尝试获取线程局部变量映射
        Map<String, Object> map = THREAD_LOCAL.get();
        // 如果映射为空,则创建一个新的ConcurrentHashMap
        if (Objects.isNull(map)) {
            map = new ConcurrentHashMap<>();
            // 将新创建的映射设置为线程局部变量
            THREAD_LOCAL.set(map);
        }
        // 返回线程局部变量映射
        return map;
    }
}

LoginUtil:

/**
 * 登录工具类,提供获取当前登录用户ID的方法。
 * 该类的设计目的是为了在系统中方便地获取当前登录用户的标识,以便进行权限检查或其他需要用户身份信息的操作。
 */
public class LoginUtil {

    /**
     * 获取当前登录用户的ID。
     * 本方法通过调用LoginContextHolder中的getLoginId方法来实现,具体实现了如何从当前的上下文中获取登录用户ID的逻辑。
     * 对于调用者来说,无需关心具体的实现细节,只需通过本方法即可获取到当前登录用户的ID。
     *
     * @return 当前登录用户的ID,以字符串形式返回。
     */
    public static String getLoginId() {
        return LoginContextHolder.getLoginId();
    }

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果不使用ThreadLocal,将用户上下文信息作为参数传递到Spring AOP的切面中,可以通过以下步骤实现: 1. 定义一个用于保存用户上下文信息的类,例如: ``` public class UserContext { private String userId; private String userName; // 省略getter和setter方法 } ``` 2. 在Spring AOP的切面中定义一个`UserContext`类型的参数,并使用`@Around`注解将该参数绑定到切点上。例如: ``` @Aspect public class UserContextAspect { @Around("execution(* com.example.demo.service..*(..)) && args(.., userContext)") public Object around(ProceedingJoinPoint joinPoint, UserContext userContext) throws Throwable { try { // 在当前线程上下文中保存用户上下文信息 UserContext.setCurrentUserContext(userContext); // 执行目标方法 return joinPoint.proceed(); } finally { // 清除当前线程上下文中的用户上下文信息 UserContext.removeCurrentUserContext(); } } } ``` 在上面的代码中,`@Around`注解定义了一个环绕通知,用于将传入的用户上下文信息保存到当前线程上下文中,并将该参数绑定到切点上。在方法调用前,将用户上下文信息保存到当前线程上下文中;在方法调用后,清除当前线程上下文中的用户上下文信息。 3. 在需要使用用户上下文信息的方法中,添加一个`UserContext`类型的参数,并在调用该方法时传递当前线程的用户上下文信息。例如: ``` @Service public class UserService { public User getById(Long id, UserContext userContext) { // 从当前线程上下文中获取用户上下文信息 String userId = userContext.getUserId(); String userName = userContext.getUserName(); // 根据用户ID查询用户信息 User user = userDao.getById(id); // TODO: 根据用户上下文信息进行业务处理 return user; } } ``` 在上面的代码中,`getById`方法添加了一个`UserContext`类型的参数,并在业务逻辑中使用该参数获取当前线程的用户上下文信息,然后根据该信息进行业务处理。 通过以上步骤,就可以将用户上下文信息作为参数传递到Spring AOP的切面中,并在需要使用该信息的方法中获取。需要注意的是,在调用方法时需要手动传递用户上下文信息,否则会出现空指针异常。另外,需要在当前线程上下文中保存用户上下文信息和清除用户上下文信息,否则会出现数据混乱的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值