SpringMVC 可以用@PathVariable、@RequestParam等来接收不同的参数类型。下面研究其参数解析过程,不管是何种参数方式,都是从Request 中拿的参数。
代码如下:
User 类:
Controller 接口:
测试访问:
(1) 获取一个处理器执行器链: HandlerExecutionChain (包含Handler 和Inteceptors)。 org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
(2) 遍历处理器链获取一个HandlerAdapter 处理器适配器, 适配的类型是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter。
(3) 调用到方法开始处理:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
1. org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
2. 调用到 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
4. org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
5. org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
6. 参数解析:org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
可以看到参数解析是在这里,根据参数的个数,然后循环遍历获取参数信息。
1》单个参数的解析过程如下:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument
获取到参数解析器,然后进行参数的解析,获取参数解析器的方法如下:
可以看到先从缓存里面拿,如果缓存里面没有拿到,遍历MVC内置的一些解析器,然后判断是否支持该参数的解析,如果支持解析完存入缓存map,下次直接从缓存中拿。这个类内置了大概30个参数解析器,如下:
主要的参数解析器为:
RequestResponseBodyMethodProcessor - 解析@RequestBody和@ResponseBody 的参数
PathVariableMethodArgumentResolver- 解析@PathVariable 注解的
RequestParamMethodArgumentResolver - 解析@RequestParam 注解修饰的参数
ServletModelAttributeMethodProcessor - 解析默认不加任何注解的参数以及解析@ModelAttribute 注解修饰的参数
2》 然后开始解析参数调用的方法是参数解析器的resolveArgument 方法
分析四个解析过程:
(1)如果是RequestBody 修饰的参数:
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument
请求转交给父类:org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)
这里实际就是读取请求体中的数据,然后转换成JSON对象。
判断messageConverter 消息转换器是否能读取消息,对于普通的Bean 支持的转换器是org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 然后处理消息,也就是读取请求体重的数据,然后转换成对应的java Bean。 注意这里会交给JackSON工具包的com.fasterxml.jackson.databind.ObjectMapper#_readMapAndClose 方法。
(2) 如果是@PathVariable 修饰的参数: 请求交给org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
父类会调用子类的org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveName 方法进行解析参数:
实际就是从参数模板的Map 中根据名称获取到一个参数的值
(3) 如果是@RequestParam 注解修饰的参数:
最后也是会打到org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
请求打到:org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName。
实际就是调用request.getParameterValues 获取到参数的值
(4) 如果没有注解修饰或者@ModelAttribute 修饰的参数:
org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
1. 声明一个注解用于从标记使用自定义的参数处理器
2. 编写一个参数处理器,模拟从Header 中获取到参数之后设置值
3. 注册到SpringMVC 的参数解析器列表中
4. 编写测试Controller
上面据完成了一个简单的自定义参数解析器,从header 中读取属性。
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】