1.参数解析器介绍
参数解析器用于解析Handler方法参数,例如经常看到Handler方法签名中有HttpServletRequest、HttpServletResponse等对象,还有请求参数中有@RequestBody、@PathVariable等注解修饰的对象,这些对象都是经过参数解析器解析后注入的
SpringMVC中默认的参数解析器见RequestMappingHandlerAdapter#getDefaultArgumentResolvers
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// 基于注解的参数解析器resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// 基于类型的参数解析器resolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());// 添加自定义参数解析器if (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// 能够处理所有场景resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;
}
这些参数解析器会被封装到HandlerMethodArgumentResolverComposite中,处理参数时,会按照加入的顺序依次调用每个参数解析器执行解析任务,直到其中某个参数解析器能够解析此参数,则返回参数解析结果
2.自定义参数解析器
在某些场景下,我们可以自定义参数解析器,将某些数据通过参数传递到Handler方法中,例如在支付的场景中,我想要将渠道信息从请求头header中设置到Handler方法的参数中,这时我们可以自定义注解,然后再自定义参数解析器实现HandlerMethodArgumentResolver接口,将渠道的值传递到Handler方法中,示例如下:
1)创建Channel注解及自定义参数解析器
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Channel {
}public class ChannelArgumentResolver implements HandlerMethodArgumentResolver {// 是否支持解析此参数@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {Channel channel = methodParameter.getParameterAnnotation(Channel.class);return channel != null;}// 解析参数@Overridepublic Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {String channel = nativeWebRequest.getHeader("x-chnl");return channel;}
}
2)在配置类中加入自定义参数解析器
这样初始HandlerAdapter时,就会将自定义的参数解析器加入到参数解析器集合中
@Configuration
@ComponentScan
public class WebConfig {@Beanpublic ServletWebServerFactory servletWebServerFactory() {return new TomcatServletWebServerFactory();}@Beanpublic DispatcherServlet dispatcherServlet() {return new DispatcherServlet();}@Beanpublic DispatcherServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");registrationBean.setLoadOnStartup(1);return registrationBean;}@Beanpublic MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();// 添加自定义参数解析器handlerAdapter.setCustomArgumentResolvers(Arrays.asList(new ChannelArgumentResolver()));return handlerAdapter;}
}
3)添加控制器
@RestController
public class Controller {@GetMapping("/test")public void test(@Channel String channel) {System.out.println("渠道:" + channel);}
}
4)添加启动类
public class Test {public static void main(String[] args) {AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);}
}
启动程序后使用postman调用http://127.0.0.1:8080/test,并在请求头中加入x-chnl=google,就能够打印出渠道信息,说明自定义参数解析器能够将渠道信息设置到test方法的参数中
3.参数解析器原理
参数解析器都实现了HandlerMethodArgumentResolver接口,它提供了如下两个方法