天天看點

SpringMVC 自定義參數解析器.

一、簡述

有沒有想過像 @RequestParam、@RequestBody 這些注解的工作原理呢?為什麼 form 表單、application/json 的參數能夠直接封裝進 Bean 對象中呢?這就要說到 HandlerMethodArgumentResolver — 方法參數解析器,該接口有兩個方法:

public interface HandlerMethodArgumentResolver {

	boolean supportsParameter(MethodParameter parameter);


	Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;

}
           

supportsParameter 方法傳回 boolean 值,表示是否啟用該解析器,true 表示啟用,false 表示不啟用;resolveArgument 方法表示方法參數的解析過程,就是你把 HTTP 的請求參數轉換為方法參數的過程,傳回 Object 對象,即參數的轉換結果。

@RequestParam 對應的參數解析器是 RequestParamMethodArgumentResolver;@RequestBody 對應的參數解析器是 RequestResponseBodyMethodProcessor;諸如此類的解析器,讀者可自行閱讀。

二、自定義參數解析器

我想在控制器方法參數中得到目前使用者的登陸資訊,大概是如下這個效果,隻要添加了 @CurrentUser 注解,那麼 UserParam 參數中就會有目前使用者的登陸資訊。

@RequestMapping(value = "/list", method = RequestMethod.POST)
    public ResponseData<List<SysWayDto>> list(@CurrentUser UserParam userParam) {
        Map<String, Object> objectMap = MapUtils.convertObjToMap(userParam);
        return sysWayService.findWithOriginDest(objectMap);
    }
           

怎麼實作呢?首先我們需要有一個 @CurrentUser 注解:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}
           

那麼剩下來的工作就是 HandlerMethodArgumentResolver 的事情了:

@Slf4j
public class CurrentUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * 用于判定是否需要處理該參數分解,傳回 true 為需要,并會去調用下面的方法resolveArgument。
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType().isAssignableFrom(UserParam.class)
                && methodParameter.hasParameterAnnotation(CurrentUser.class);
    }

    /**
     * 真正用于處理參數分解的方法,傳回的 Object 就是 controller 方法上的形參對象。
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        String accessToken = nativeWebRequest.getHeader(GatewayHeader.accessToken);
        try {
            UserParam userParam = JsonUtils.readValue(JwtHelper.parseJWT(accessToken), UserParam.class);
            return userParam ;
        } catch (IOException e) {
            log.error("CurrentUserHandlerMethodArgumentResolver resolveArgument readValue error.accessToken:{}", accessToken, e);
        } catch (Exception e) {
            log.error("CurrentUserHandlerMethodArgumentResolver resolveArgument error.accessToken:{}", accessToken, e);
        }
        return null;
    }
}
           

最後,别忘了注冊上自定義的參數解析器哦!

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new CurrentUserHandlerMethodArgumentResolver());
    }
}