回到DispatcherServlet的doDispatch中,在获取
mappedHandler = getHandler(processedRequest);
是一个清晰的逻辑概念,即从一个request的路径及其它属性获取到某个controller+method为一个handler主体+拦截器列表,该逻辑放在后期介绍。大家会有一个一个疑问,handler(HandlerMethod)是谁负责调用,以及谁去解析@RequestBody,@PathVariable,@RequestParam这些注解,那就开始吧。
spring-mvc围绕着责任链模式做请求的一步步处理,先来看spring-mvc最最核心的接口HandlerAdapter,其中定义了三个方法:
1:boolean supports(Object handler); 2:ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; 3:long getLastModified(HttpServletRequest request, Object handler);
其中3不做展开,前两个方法的意思就是:我是否支持,我支持则我处理。我们都围绕着99%用的@Controller展开,对应的handler为HandlerMethod(对应controller#method),该handerle的适配器为:RequestMappingHandlerAdapter,该类的父类+也比较简单,无核心功能。其中方法1也无需展开,大概意思就是:handler为HandlerMethod,一看代码便知:
public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
其中handler没有任何继承及接口,构造方法接收某controller的method:
public HandlerMethod(Object bean, Method method) {
最核心的方法handle放在最后说明,因handler明确为HandlerMethod,因此该方法在抽象父类中做了转换后新定义了一个方法:
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)
handleInternal中处理了是否相同session同步请求及响应头Cache-Control的处理,这个从来没有用到过该逻辑,所以也不做展开,该方法中调用了该核心方法并返回ModelAndView:
mav = invokeHandlerMethod(request, response, handlerMethod);
处理逻辑都在该方法中:invokeHandlerMethod。
ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod)
该类继承HandlerMethod,设置参数处理器argumentResolvers(HandlerMethodArgumentResolverComposite),设置返回值处理器returnValueHandlers(HandlerMethodReturnValueHandlerComposite)。所以到这里,也基本了解了一个整体的过程,该方法参数处理器和方法返回值处理器都来自RequestMappingHandlerAdapter。
整体的处理过程已经有个轮廓了吧,现在再继续往下介绍细节,先说方法参数处理器吧,适配器中的argumentResolvers是什么时候初始化的,初始化了哪些参数处理器,如果我要增加参数处理器我应该怎么处理?spring-mvc在spring容器中运行,直接看改方法就一目了然:
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
在getDefaultArgumentResolvers,他都定义好了近20个内置的处理器,其中getCustomArgumentResolvers一看便知,是框架给开发预留的自定义处理器。其实这么多实现不可能一个个去了解,我们只需要看他接口是怎么定义的,以及我们常用的@RequestBody,@PathVariable,@RequestParam,及不带注解的基本类型加对应类型怎么处理,就已经足够掌握了。先看接口定义:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory);
}
其中supportsParameter是不是该我处理,其中parameter也一看便知:参数,只是spring将参数的class+注解都预先组织好了。resolveArgument接收参数,将request中的String值取到转换成真正的参数值返回。当前rest方式横行,大家都习惯了用@RequestBody去接收参数,那就先介绍这个参数值怎么处理的吧。文档写到这里,其实我也不知道是哪个参数处理器处理的,那就去找吧:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// Annotation-based argument resolution
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());
// Type-based argument resolution
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());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
是不是这个比较像啊:RequestResponseBodyMethodProcessor。那点进去看看
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
找到了吧,是否包含这个注解:parameter.hasParameterAnnotation(RequestBody.class),顺便也把这个也顺带出来了:supportsReturnType,一看便知,这个类充当了两个角色,那就看看这个类的继承结构:它吧响应的处理逻辑也一直做了吧,大概是同一个人写的不想写两次吧,以及这个请求参数和响应处理它都用到了json转换。
言归正传,这里还只是说参数是怎么处理的:
resolveArgument,这之中它调用了readWithMessageConverters,其中将servlet规范定义的httpServletRequets转成成了ServletServerHttpRequest,该类定义了获取请求流的方法:
public InputStream getBody()
再调用父类中的AbstractMessageConverterMethodArgumentResolver方法:
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType)
看名字是一样的,不过接收的参数不同,这个里面的处理逻辑有点长,但凭想象也知道,是从HttpInputMessage获取字符串,该字符串肯定是一个json吧,通过HttpMessageConverter将json字符串转换成对象返回。
其中messageConverters在抽像类中定义的原因也知道了,因为它同时还要处理@ResponseBody的json转换,因此也实现HandlerMethodReturnValueHandler。其中转换器在哪定义的呢,往上翻去找RequestResponseBodyMethodProcessor的定义,是在RequestMappingHandlerAdapter顶级类型定义的:
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
其中getMessageConverters()的默认值在中定义:WebMvcConfigurationSupport
#addDefaultHttpMessageConverters 其中用哪个json转换器,看该静态代码块便知,记得用这个命令查找mvn dependency:tree依赖
static {
ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}
依赖中默认带入的是:
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
看该类的构造方法也知道了这个处理器是怎么处理的的了吧:
public MappingJackson2HttpMessageConverter() {
this(Jackson2ObjectMapperBuilder.json().build());
}
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}
所以记得发送请求的时候,在请求头中设置:
Content-Type:application/json