springboot接口开发框架——通过拦截器和threadlocal保存用户信息

定义请求头

在实际开发过程中,一般把用户的一些基本信息放到http请求头,所以我们先定义一个model来保存请求头中的信息

public class UserHeader {

  private String userId;

  private String version;

}

定义threadlocal

ThreadLocal定义为static类型

public class UserHeaderHodler {

  public static ThreadLocal<UserHeader> threadLocal = new ThreadLocal<>();

  /**
   * 需要兼容userHeader为null的情况
   * @return
   */
  public static String getUserId() {
    return Optional.ofNullable(threadLocal.get())
            .map(userHeader -> userHeader.getUserId())
            .orElse("");
  }

  public static String getVersion() {
    return Optional.ofNullable(threadLocal.get())
            .map(userHeader -> userHeader.getVersion())
            .orElse("");
  }

}

定义拦截器

定义一个拦截器,拦截每次http请求,获取到http请求头的内容,保存到threadlocal,方便具体业务逻辑中使用这些信息

public class UserHeaderIntercepter implements HandlerInterceptor {

  /**
   * 一次http方法调用都是在一个线程完成的
   * 因为本次请求的很多地方都可能会用到userId
   * 所以放在threadLocal可以减少参数的传递
   *
   * @param request
   * @param response
   * @param handler
   * @return
   * @throws Exception
   */
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    UserHeader userHeader = UserHeader.builder()
            .userId(request.getHeader("userId"))
            .version(request.getHeader("version"))
            .build();
    UserHeaderHodler.threadLocal.set(userHeader);
    return HandlerInterceptor.super.preHandle(request, response, handler);
  }

  /**
   * 本次调用结束了,就可以释放threadLocal资源避免内存泄漏的情况发生
   * @param request
   * @param response
   * @param handler
   * @param ex
   * @throws Exception
   */
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    UserHeaderHodler.threadLocal.remove();
    HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
  }
}

在拦截器的afterCompletion的方法中执行了remove方法,为了避免线程执行结束但是没有销毁,比如线程属于线程池的情况,这样线程一直持有对象,如果对象很大,就会造成内存泄漏,从而导致内存溢出

threadlocal保存的信息使用

获取基本用户请求头信息属于一个可以共用的逻辑,在许多moduel都会用到,因此我们把它封装到一个通过moduel里,其他项目通过dependency的方式使用
在这里插入图片描述
看一下controller这个moduel中的service方法是如何使用拦截器中保存的http请求头信息的

@Service
public class Service1 {

  /**
   * 拦截器和service方法同处于一个线程,所以可以通过这种方式,保证userHeader的线程安全
   * 同时避免参数多次传递
   * @return
   */
  public String m1() {
    return UserHeaderHodler.getUserId() + "====>" + UserHeaderHodler.getVersion();
  }

}

最终达到了使用threadlocal的两个目的:

  1. 线程安全,每次请求都在一个线程中完成,恰好threadlocal保存的信息属于每个线程所独有,threadlocal保存的信息其实是和每个线程绑定的,完全避免了变量共享带来的线程安全问题
  2. 减少参数传递,本次请求的任何方法不管是controller还是service层,又或者是dao层都可以使用threadlocal来获取用户相关的信息

运行结果

idea有个方便好用的工具:reatfulTool,我们使用他来验证代码效果
在这里插入图片描述
先设置请求头
在这里插入图片描述
点击send按钮之后看到了我们想要的效果
最终实现了通过拦截器获取http请求头信息,保存到threadlocal,提供给具体的业务逻辑使用

github demo,路过记得star,支持一下😄

拦截器的定义和加载也有一些知识点需要学习,可以加深对springboot框架的理解,在下一篇文章中,我们会继续分析拦截器的定义

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值