ThreadLocal的使用

ThreadLocal 一般不会单独使用,基本上都是 放在一个工具类中,然后在拦截器中去使用(至少存储 Token 的时候是这样的)

这里为了更加还原真实的线上环境,直接就用了 拦截器+统一返回+全局异常捕获+ThreadLocal,项目还是比较完整的

首先创建一个项目 启动后看项目是否能跑通 这个就很简单了,直接略过

创建 ThreadlLocal 的工具类

public class TokenUtils {
    // 通过 ThreadLocal 存储 token
    private static final ThreadLocal<String> tokenThreadLocal = new ThreadLocal<>();

    // 设置 token
    public static void setToken(String token) {
        tokenThreadLocal.set(token);
    }

    // 获取 token
    public static String getToken() {
        return tokenThreadLocal.get();
    }

    // 清除 token
    public static void clearToken() {
        tokenThreadLocal.remove();
    }
}

编写拦截器 并注册到 容器中

//一个拦截器,拦截请求,把请求的时间记录下来,并将请求头中的 token 拿出来
public class MyInterceptor implements HandlerInterceptor {

    @Override//在请求处理之前进行调用(Controller方法调用之前)
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //记录请求时间
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        //将请求头中的 token 拿出来
//        String token = request.getHeader("token");
        //从 Authorization 中获取 token 并去除 Bearer 字符串
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        //将 token 放入 ThreadLocal 中
        TokenUtils.setToken(token);
        return true;
    }

    @Override//请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //获取请求时间
        long startTime = (long) request.getAttribute("startTime");
        //计算请求处理时间
        long endTime = System.currentTimeMillis();
        System.out.println("本次请求处理时间为:" + (endTime - startTime) + "ms");
    }

    @Override//在整个请求结束之后被调用,也就是在 DispatcherServlet 渲染了对应的视图之后执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除 ThreadLocal 中的 token
        System.out.println("清除 token" );
        TokenUtils.clearToken();
    }
}

注意,HandlerInterceptor 并不能让你 对返回值进行一些处理和修改,如果你想对返回值做处理和修改,你需要去了解的是 ResponseBodyAdvice

@Configuration
public class MyWebConfig implements WebMvcConfigurer {

    //注册 MyInterceptor 拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/public/**"); // 排除"/public"下的所有请求
    }
}

创建一个类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyStudent
{
    private String name;
    private int age;
}

统一的返回值

@Data
// 统一返回类
public class UnifiedReturn<T> {

    private int code;// 状态码  200正常  各种异常 需要在 枚举里面定义
    private String msg;// 返回信息
    private T data;// 返回数据

    public UnifiedReturn(T data) {
        this.data = data;
    }

    //success
    public static <T> UnifiedReturn<T> success(T data) {
        UnifiedReturn<T> aReturn = new UnifiedReturn<>(data);
        aReturn.setCode(UnifiedReturnEnum.SUCCESS.getCode());
        aReturn.setMsg(UnifiedReturnEnum.SUCCESS.getMsg());
        return aReturn;
    }

    //error
    public static <T> UnifiedReturn<T> error(int code, String msg) {
        UnifiedReturn<T> aReturn = new UnifiedReturn<>(null);
        aReturn.setCode(code);
        aReturn.setMsg(msg);
        return aReturn;
    }
}

返回值的枚举

// 统一返回 枚举
public enum UnifiedReturnEnum {

    SUCCESS(200, "成功"),
    FAIL(500, "失败"),
    NO_PERMISSION(403, "无权限"),
    NOT_FOUND(404, "未找到"),
    PARAM_ERROR(400, "参数错误");

    private int code;
    private String msg;

    UnifiedReturnEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

统一的 异常捕获

@ControllerAdvice//全局异常处理
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody  //返回json  这边是一定要加的
    public UnifiedReturn<String> exceptionHandler(Exception e) {
        return UnifiedReturn.error(UnifiedReturnEnum.FAIL.getCode(), e.getMessage());
    }
}

控制器

@RestController
@RequestMapping("/basic")
public class BasicController {

    @GetMapping("/hello")
    public UnifiedReturn<MyStudent> hello() {
        String token = TokenUtils.getToken();//该方法想在哪里用都可以,只要在同一个线程中 都能获取到 token
        System.out.println("token: " + token);
        return UnifiedReturn.success(new MyStudent("theonefx", 18));
    }

    //测试异常处理--这里测试的是 运行时异常
    @GetMapping("/exception")
    public UnifiedReturn<String> exception() {
       int i = 1/0;
       return UnifiedReturn.success("success");//不会执行到这里
    }

    @GetMapping("/exception2")//这里测试的是 主动抛出的异常
    public UnifiedReturn<String> test() {
       throw new RuntimeException("test exception");
    }

}

请求看一下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal是一个Java类,用于在多线程环境下保存线程本地变量的副本。通过创建ThreadLocal对象,每个线程都可以拥有自己独立的变量副本,互不干扰。 使用ThreadLocal的过程如下: 1. 创建一个ThreadLocal对象,可以指定泛型类型。 2. 在每个线程中,通过ThreadLocal对象的`get()`方法获取当前线程的变量副本。如果当前线程没有设置过变量值,则会使用默认值进行初始化。 3. 在每个线程中,通过ThreadLocal对象的`set(value)`方法设置当前线程的变量值。 4. 在每个线程中,通过ThreadLocal对象的`remove()`方法移除当前线程的变量副本。 需要注意的是,ThreadLocal对象的生命周期与Thread对象的生命周期一样长。当ThreadLocal对象被垃圾回收时,关联的变量副本也会被回收。 在ThreadLocal内部,使用ThreadLocalMap来存储每个线程的变量副本。ThreadLocal的实例作为key,变量值作为value。ThreadLocalMap可以使用强引用或弱引用来引用ThreadLocal对象。如果使用强引用,当ThreadLocal对象被回收时,如果没有手动删除对应的变量副本,会导致内存泄漏。如果使用弱引用,当ThreadLocal对象被回收时,对应的变量副本也会被回收。 总结来说,ThreadLocal是一种线程本地变量,通过保存每个线程的变量副本,实现了多线程环境下的线程隔离。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ThreadLocal使用与原理](https://blog.csdn.net/qq_35190492/article/details/116431270)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [ThreadLocal使用详解](https://blog.csdn.net/LJJZJ/article/details/88763666)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值