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");
}
}
请求看一下