The problem

In a project developed by the maintenance company, it was found that many places needed to obtain the current logged-in user object, and the user information was obtained from Redis according to the token value in the request header, so the code similar to the following appeared in many places

public Object commentAdd(TravelComment comment, HttpServletRequest request){
    // Obtain user information based on the token
    String token = request.getHeader("token");
    UserInfo user = userInfoRedisService.getUserByToken(token);

    / /... Other code
    return JsonResult.success();
}
Copy the code

How do you solve the problem of writing an HttpServletRequest Request every time and then retrieving the current login object based on the token in the request header?

The solution

In the request mapping method list declaration, you can get the current login user object, code as follows

public Object commentAdd(TravelComment comment, UserInfo userInfo){
    UserInfo user = userInfo;

    / /... Other code
    return JsonResult.success();
}
Copy the code

How do you do this? This is done by customizing SpringMVC’s parameter parser, since the existing SpringMVC parameter parser cannot be used to meet our requirements

Create a parameter resolver

/** * Parses the UserInfo type parameter in the request mapping method list to the current logged-in user object */
public class UserInfoArgumentResolver implements HandlerMethodArgumentResolver {
    @Autowired
    private IUserInfoRedisService userInfoRedisService;
    // Determine which parameter types are supported by the current parser, returning true to indicate that they are supported
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType() == UserInfo.class;
    }
    // Resolver rules:
    // Here the UserInfo type parameter is parsed into the current logged-in user object.
    // execute when the supportsParameter method returns true
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        String token = request.getHeader("token");
        returnuserInfoRedisService.getUserByToken(token); }}Copy the code

Add a custom parameter parser to the Spring container

@Configuration
public class WebConfig implements WebMvcConfigurer{

    // Custom user resolver
    @Bean
    public UserInfoArgumentResolver userInfoArgumentResolver(a){
        return new UserInfoArgumentResolver();
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(userInfoArgumentResolver()); }}Copy the code

All of the above does achieve the desired result, but there are still some problems. Not all request mapping methods have UserInfo in the list. UserInfo must go through a custom parameter parser, so how can we distinguish between using a framework parser and a custom parameter parser?

Use custom annotations to distinguish

Add custom annotations to the list of request mapping methods that require a custom parser

/** * User parameter injection annotation * paste the annotation user parameter using a custom parameter parser */
@Target({ElementType.PARAMETER})  // indicates to attach to the parameter
@Retention(RetentionPolicy.RUNTIME)
public @interface UserParam {
}
Copy the code

Interface code improvement

public Object commentAdd(TravelComment comment, @UserParam UserInfo userInfo){
    UserInfo user = userInfo;

    / /... Other code
    return JsonResult.success();
}
Copy the code

That’s the final solution!