Servlet API参数解析原理
springMVC除了给我们参数位值传输一些简单注解(@PathVariable
,@RequesrHeader
,@MedelAttibute
,@RequestParam
,@MatrixVariable
,@CookieValue
,@RequestBody
)能参数解析外。我们方法位值也允许传入一些Servlet API
(WebRequest
,ServetRequest
,MultipartRequest
,HttpSession
,javax.servlet.http.PushBuilder
,Principal
,ImputStream
,Reader
,HttpMethod
,Locale
,TimeZone
,Zoneld
)作为参数,
比如我们写个RequestController.java
:
@Controller //普通的Controller,它只是我们的一个控制器
public class RequestController {
//如果是控制器我们默认方法的返回时进行页面跳转的
@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
request.setAttribute("msg","成功了");
request.setAttribute("code",200);
return "forward:/success"; //转发到 /success请求
}
}
里面有一个/goto
方法,传入了原生的request
对象。request.setAttribute("msg","成功了");
我们打上断点Debug一下,我们来看一下我们的request
对象是用哪个参数解析器来进行处理的;我们发送localhost:8080/goto
请求:一路step over
来到我们WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
,Resume Program
(F9
)到我们执行方法的Object returnValue = invokeforRequest(webRequest,mavContainer,providedArgs);
(获取我们的参数值)steo into
拿到我们所有参数(MethodParamrter[] parameters = getMethodParameters();
)然后拿到我们所有参数,拿到我们的第一个参数(parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
)我们接下来看resolvers supports parameter
(if(!resolvers.supportsParameter(parameter))
)接下来就要看我们这些解析器能不能解析我这个类型(com.huihu.boot.controller.goToPage(javax.servlrt.http.Servlet.http.HttpServletRequest)
)的参数,这个类型的参数能够明显看到这个参数是HttpServletRequest
,我们看它能不能解析(step into
):
我们这所有的参数解析器挨个遍历断点打到(for(HadlerMethodArgumentResolver resolver : this.argumentResolvers)
中的if(resolver.supportParameter(parameter))
)遍历的第一个
是叫RequestParamMethodArgumentRequest
,放行到ServletRequestMethodArgumentResolver
支持。
拿到这个解析器(HandlerMethodArrgumentResolver resolver = getArgumentResolver(parameter);
)调用解析(if(resolver == null)
)它怎么把原生的Request
传过来
在HandlerMethodArgumentResolverComposite.java
里有一个方法public Object resolveArgument(MethodParameter parameter,@Nullable ModelAndViewContainer mavCon...,
NativeWebRequest webRequest,...)throws Exception{
}
的一个形参webRequest
相当于把Request
和response
原生的这俩个东西全部组合放到了这个对象里面(NativeWebRequest webRequest
)然后解析参数的时候,我们现在想要拿到原生的Request对象
(Class<?> paramType = paramter.getParameterType();
)它就在这判断(if(WebRequest.class.isAssignableFrom(paramType))
)对象类型(paramType
)是不是WebRequest
,我们不是,而我们是ServletRequest
就对了(if(ServletRequest.class.isAssignableFrom(paramType))
)然后他给我们解析step into
(他把webRequest
(return resolveNativeRequest(webRequest,paramType)
)传进来)也就是webRequest
把我们这个原生的request
请求拿到(T nativeRequest = webRequest.getNativeRequest(requiredType);
)然后把原生的request
请求往回一返(return nativeRequest;
)就行了
WebRequest
,ServetRequest
,MultipartRequest
,HttpSession
,javax.servlet.http.PushBuilder
,Principal
,ImputStream
,Reader
,HttpMethod
,Locale
,TimeZone
,Zoneld
ServletRequestMethodArgumentResolver
以上的部分参数
其他的一些参数大家自己来找相应的解析器就行了
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
(Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}