在做Spring MVC
时,我们只需用@Controllor
来标记Controllor
的bean,再用@RequestMapping
来标记需要接受请求的方法,就可以根据网页请求中的参数名,自动绑定到POJO对象的属性名,这是相当方便的。其中的原理是什么呢?
前瞻
通过Spring MVC请求调用链,实际是由ServletInvocableHandlerMethod.invokeAndHandle(webRequest, mavContainer)
方法执行处理。
|
|
webRequest
为httpRequest
包装后的类,添加了uri成员,标记请求uri以便方便分配。mavContainer
为ModelAndViewContainer
类对象,用于绑定模块(POJO)和视图(view即网页)。
|
|
在invokeForRequest
方法中,Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs)
;该方法获取每个请求参数与对应的值
|
|
这个方法也还是比较简单的,循环遍历请求参数,然后委托给HandlerMethodArgumentResolver
接口的方法 resolveArgument
去进行参数值解析。
绑定参数名
这里面对ParameterNameDiscovery
初始化,用来查找参数名:
|
|
低于java1.8使用new LocalVariableTableParameterNameDiscoverer()
来解析参数名。
|
|
通过map = inspectClass(declaringClass);
获取名称map。
|
|
ClassReader
位于org.springframework.asm
包中,是Spring用于反编译的包,读取class信息,class信息中是包含参数名的(可以用文本编辑器打开一个class文件查看,虽然有乱码,但是方法的参数名还在)。
通过accept填充map对象,map的键为成员名(方法名或者参数名),值为参数列表(字符串数组)。
由此可见,Spring是直接读取class文件来读取参数名的.
注意:this.parameterNamesCache.put(declaringClass, map);
使用了ConcurrentHashMap
设置方法名缓存。避免每一次调用都要反编译class文件。
解析参数值
要给参数注入参数的值,最终是通过HandlerMethodArgumentResolver
接口的实现类HandlerMethodArgumentResolverComposite
实现的。HandlerMethodArgumentResolverComposite
类实现是采用的组合模式的设计策略,可以在类RequestMappingHandlerAdapter
中的方法afterPropertiesSet
方法中看到HandlerMethodArgumentResolverComposite
中的resovler的初始化代码。
|
|
通过这个afterPropertiesSet
方法增加默认的参数解析器,默认的参数解析器如下:
|
|
常用的参数解析器由RequestParamMethodArgumentResolver
, RequestResponseBodyMethodProcessor
(这个类也实现了HandlerMethodReturnValueHandler
接口)等。
参数解析源码说明如下:
- 如果有提供的参数值,直接返回提供的参数值
|
|
- 检察参数类型是否支持
|
|
|
|
|
|
- 查询参数解析器,找到后委托给参数解析器解析
|
|
|
|
|
|
然后会执行父类(AbstractMessageConverterMethodArgumentResolver)的readWithMessageConverters方法。
|
|
这里是用了消息转换器HttpMessageConverter
- 如果未找到抛出异常
|
|