如何l利用`ThreadLocal`、`HandlerInterceptor`、`HandlerMethodArgumentResolver`来完成代码优化

发布时间 2023-11-16 17:10:42作者: zzusjw

核心类 ThreadLocalHandlerInterceptorHandlerMethodArgumentResolver

1. ThreadLocal
2. WebMvcConfigurer 
	- addArgumentResolvers
3. HandlerMethodArgumentResolver
	- supportsParameter
	- resolveArgument
  • ThreadLocal:可以理解为一个线程安全的Map。

    // 用户上下文
    public class UserContext {
    
        private static ThreadLocal<TUser> userThreadLocal = new ThreadLocal<>();
    
        public static void setUser(TUser tUser) {
            userThreadLocal.set(tUser);
        }
    
        public static TUser getUser() {
            return userThreadLocal.get();
        }
        
        public static void remove(){
            userThreadLocal.remove();
        }
    }
    
  • HandlerInterceptor:接口是Spring MVC中的一种拦截器,用于在请求处理过程中进行预处理和后处理操作。

    HandlerInterceptor接口定义了三个方法,分别是:
    
    1. `preHandle`: 在请求处理方法之前执行。这允许你在请求被处理之前执行一些操作,例如验证用户身份或日志记录。
    		- 一般我们接口都需要带token,会获取用户信息。
    		- 我们可以在`preHandle`方法中,放入user。
    		- UserContext.setUser(tUser);
    
    2. `postHandle`: 在请求处理方法之后,视图渲染之前执行。这允许你在返回响应之前修改模型数据或视图。
    
    3. `afterCompletion`: 在整个请求处理完成之后执行。这是在响应已经被发送给客户端之后执行的方法,通常用于清理资源或执行一些最终的操作。
    		- 请求结束之后,可以使用 UserThreadLocal.remove(); 
    		- 好处:避免内存泄露。
    		- 原因:ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。
    
  • HandlerInterceptor注册进WebMvcConfigurer

    @Configuration
    public class WebMVCConfig implements WebMvcConfigurer {
        @Autowired
        private LoginInterceptor loginInterceptor;
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 将处理器拦截器注册进WebMvcConfig
            registry.addInterceptor(loginInterceptor);
        }
    }
    
  • UserThreadLocal总结

    1. 在本次请求中,任何地方用到user的地方,我们都可以使用:SysUser sysUser = UserThreadLocal.get();
    
  • 思考

    1. 每次获取user对象,都需要写:SysUser sysUser = UserThreadLocal.get();
    2. 能不能不写?
    
  • 解决办法:HandlerMethodArgumentResolver-处理方法参数解析器

    @Component
    public class UserArgumentResolver implements HandlerMethodArgumentResolver {
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            Class<?> parameterType = parameter.getParameterType();
            // 只有返回`true`时,`resolveArgument`方法才会执行。
            return parameterType == TUser.class;
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws 	 			Exception {
            	return UserContext.getUser();
        }
    }
    
  • UserArgumentResolver注册进WebMvcConfigurer

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Autowired
        private UserArgumentResolver userArgumentResolver;
        
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(userArgumentResolver);
        }
    }
    
  • 代码实例

    // 这时候,请求不需要传user参数,我们的接口也能获取到user
    @Slf4j
    @GetMapping(value = "/test")
    public void test(TUser user) {
        log.info("user:{}",user);
    }
    
  • 总结

    1. 每次请求过来,都会经过`HandlerMethodArgumentResolver`-方法参数解析器,当参数类型为,`TUser`时,会从`ThreadLocal`中获取`TUser`,赋值给接口中的`user`参数。