自定义注解获取用户信息

310 阅读1分钟

描述

该实现方法是利用了ThreadLocal隔离线程的作用。主要是做数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的,在多线程环境下,防止变量被其它线程篡改。需要注意的事,因为ThreadLocal的key基础了WeakReference弱引用,所以在使用完之后,尽量进行手动释放,避免内存泄露的问题。

maven依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.11.5</version>
    <scope>compile</scope>
</dependency>

自定义注解

/**
 * 自定义用户注解
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
    
}

用户实体

/**
 * 实体
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
@Data
public class UserInfo {
    
    private String userId;

    private String username;

}

定义获取用户工具类

/**
 * 定义使用ThreadLocal获取当前线程用户的信息
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
public class UserInfoUtil {
    /**
     * ThreadLocal的作用主要是做数据隔离,
     * 填充的数据只属于当前线程,
     * 变量的数据对别的线程而言是相对隔离的,
     * 在多线程环境下,如何防止自己的变量被其它线程篡改。
     */
    private static ThreadLocal<UserInfo> local = new TransmittableThreadLocal<>();

    public static void set(UserInfo userInfo) {
        local.set(userInfo);
    }

    public static UserInfo get() {
        return local.get();
    }

    /**
     * key被设计成弱引用
     * ThreadLocal在没有外部强引用时,发生GC时会被回收
     * 线程就会一直持续运行,value就得不到回收,发生内存泄露
     */
    public static void remove() {
        local.remove();
    }


}

配置mvc拦截器

参数解析器

/**
 * 创建参数解析器
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
@Component
public class UserInfoArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * 当返回值为true时,调用resolveArgument()方法
     *
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(CurrentUser.class) && UserInfo.class.isAssignableFrom(methodParameter.getParameterType());
    }

    @Override
    public UserInfo resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        assert request != null;
        // 获取token,根据业务对token的校验
        String token = request.getHeader("token");
        // 也可从隔离线程中获取
        // UserInfo userInfo = UserInfoUtil.get();
        // 模拟解析token,获取用户信息
        return userInfo(token);
    }

    /**
     * 模拟数据库用户数据
     *
     * @return
     */
    private UserInfo userInfo(String token) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("张三");
        userInfo.setId("123456");
        return userInfo;
    }

}

定义拦截器

/**
 * 定义拦截器
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
@Component
public class UserInfoInterceptor implements HandlerInterceptor {


    /**
     * 预处理回调
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // cookie或者header获取到token
        String token = request.getHeader("token");

        // 解析用户信息
        UserInfo userInfo = userInfo(token);

        // 存放到隔离线程
        UserInfoUtil.set(userInfo);

        // 放行
        return true;
    }

    /**
     * 后处理回调,实现处理器后,但在渲染视图前
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 整个请求处理完毕,即在视图渲染完毕回调
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 关闭threadLocal,防止内存泄露
        UserInfoUtil.remove();
    }

    /**
     * 模拟数据库用户数据
     *
     * @return
     */
    private UserInfo userInfo(String token) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("张三");
        userInfo.setId("123456");
        return userInfo;
    }


}

配置mvc

/**
 * 配置mvc,将定义好的拦截器和参数解析器配置到mvc中
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private UserInfoArgumentResolver userInfoArgumentResolver;

    @Autowired
    private UserInfoInterceptor userInfoInterceptor;

    /**
     * 将自定义的参数解析器添加到MVC解析器集合中,使其生效
     *
     * @param resolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userInfoArgumentResolver);
    }


    /**
     * 添加拦截器
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInfoInterceptor).addPathPatterns("/**");
    }
}

实践,在controller获取用户

/**
 * 用户管理控制层
 *
 * @author 苦瓜不苦
 * @date 2021/8/4
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/get")
    public UserInfo get(@CurrentUser UserInfo userInfo) {
        // 通过注解获取用户信息
        // 通过ThreadLocal隔离线程获取用户信息
        userInfo = UserInfoUtil.get()
        return userInfo;
    }

}