ThreadLocal在SpringBoot项目中的应用场景

场景:

接手一个项目,用户信息是用Feign调用用户中心获取,方法之间相互调用使用用户信息很麻烦(1,通过接口传参,2,再次调用Feign)。所以使用ThreadLocal存放用户信息

1,定义用户实体:

@ApiModel("登录用户信息")
@Data
public class FeginUser implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "用户ID")
    private String id;
    @ApiModelProperty(value = "用户名")
    private String name;
   
    @ApiModelProperty(value = "密码")
    @JsonIgnore
    private String upass;
}

2,定义工具类操作ThreadLocal(存放,获取,删除用户信息)

  public class ThreadLocalUtil {

        /**
         * 保存用户对象的ThreadLocal  在拦截器操作 添加、删除相关用户数据
         */
        private static final ThreadLocal userThreadLocal = new ThreadLocal();

        /**
         * 添加当前登录用户方法  在拦截器方法执行前调用设置获取用户
         * @param user
         */
        public static void addCurrentUser(FeginUser user){
            userThreadLocal.set(user);
        }

        /**
         * 获取当前登录用户方法
         */
        public static FeginUser getCurrentUser(){
            return userThreadLocal.get();
        }


        /**
         * 删除当前登录用户方法  在拦截器方法执行后 移除当前用户对象
         */
        public static void remove(){
            userThreadLocal.remove();
        }
    }

3,拦截器:1,访问接口时将用户信息放入ThreadLocal,2,访问结束时候删除ThreadLocal中信息(线程放入线程池并不一定会销毁)

@Component
@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor  {

    @Autowired
    private UserInfoUtil userInfoUtil;

    /**
     * 请求执行前执行的,将用户信息放入ThreadLocal
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        FeginUser user;
        try{
             user = userInfoUtil.getUser();
        }catch (CustomException e){
            log.info("***************************用户未登录, ThreadLocal无信息***************************");
            return true;
        }
        if (null!=user) {
            log.info("***************************用户已登录,用户信息放入ThreadLocal***************************");
            ThreadLocalUtil.addCurrentUser(user);
            return true;
        }
        log.info("***************************用户未登录, ThreadLocal无信息***************************");
        return true;
    }

    /**
     * 接口访问结束后,从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 {
        log.info("***************************接口调用结束, 从ThreadLocal删除用户信息***************************");
        ThreadLocalUtil.remove();
    }

4,配置拦截器。

@Configuration
@ComponentScan
public class MyAppConfigurer extends WebMvcConfigurationSupport {

    @Autowired
    private UserInfoInterceptor userInfoInterceptor;

    /**
     * 拦截器,将用户信息放入threadLocal
     *
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.userInfoInterceptor).addPathPatterns("/**");
        super.addInterceptors(registry);
    }

}

5,定义用户信息具体操作接口:

为了类实现接口后直接使用(不定义为基础类,是因为类单继承)

public interface IBaseUserInfo {
    default Boolean isLogin() {
        return ThreadLocalUtil.getCurrentUser() != null;
    }

    default FeginUser getUser() {
        return ThreadLocalUtil.getCurrentUser();
    }

    default String getUserId() {
        if (ThreadLocalUtil.getCurrentUser() != null) {
            return ThreadLocalUtil.getCurrentUser().getId();
        }
        return null;
    }

}

6,使用:(代码中第二种使用方法,不需要定义接口)

@Service
public class ApplylServiceImpl implements IBaseUserInfo {
    

    public void applyUserInfo() {
        /**
         * 1,实现接口后,直接使用
         */
        FeginUser user1 = getUser();
        /**
         * 2,不实现接口,调用ThreadLocalUtil
         */
        FeginUser user2 = ThreadLocalUtil.getCurrentUser();
    }

}

总结

        这种获取用户基础信息的场景非常多,比如项目当中某一个新增接口或者修改接口,需要保存操作日志,那么肯定需要操作人的编码和姓名,那么这个编码和姓名可以放在前端的Cookies中,每次请求的时候携带在请求头中,然后后端接收到后通过ThreadLocal进行一个用户基础信息的保存,然后在整个请求链路当中都是一个线程,那么随时都可以通过这个ThreadLocal进行用户基础信息的访问,很方便,也很直观,现在所在公司也是用的这种方法。

你可能感兴趣的:(Spring,boot,java,java,spring,boot,后端,spring)