SpringMVC 源代码分析(八)--参数解析器
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 {
// 是否支持解析此参数
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
Channel channel = methodParameter.getParameterAnnotation(Channel.class);
return channel != null;
}
// 解析参数
@Override
public 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 {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
@Bean
public 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接口,它提供了如下两个方法