Spring MVC 请求处理过程详解
SpringMVC
请求处理相信大家都很熟悉了,本篇主要是基于SpringMVC
处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题。SPring MVC 相关的文章可以关注公众号「Java后端」回复「666」下载。本篇使用的Spring版本为
5.2.2.RELEASE
九大组件
SpringMVC
几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习SpringMVC
来说非常重要。
/** 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 区域解析器,用于国际化 */
private LocaleResolver localeResolver;
/** 主题解析器 */
private ThemeResolver themeResolver;
/** Handler映射信息 */
private List<HandlerMapping> handlerMappings;
/** Handler适配器*/
private List<HandlerAdapter> handlerAdapters;
/** Handler执行异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** 请求到视图的转换器 */
private RequestToViewNameTranslator viewNameTranslator;
/** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */
private FlashMapManager flashMapManager;
/** 视图解析器 */
private List<ViewResolver> viewResolvers;
HandlerMapping
:Handler
映射信息,根据请求携带的url信息查找处理器(Handler
)。每个请求都需要对应的Handler
处理。HandlerAdapter
:Handler
适配器,SpringMVC
没有直接调用处理器(Handler
),而是通过HandlerAdapter
来调用,主要是为了统一Handler
的调用方式ViewResolver
:视图解析器,用来将字符串类型的视图名称解析为View
类型的视图。ViewResolver
需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。MultipartResolver
:文件上传解析器,主要用来处理文件上传请求HandlerExceptionResolver
:Handler执行异常解析器,用来对异常进行统一处理RequestToViewNameTranslator
:请求到视图的转换器LocaleResolver
:区域解析器,用于支持国际化FlashMapManager
:SpringMVC
允许重定向时携带参数,存在session
中,用完就销毁,所以叫FlashMap
ThemeResolver
:主题解析器,用于支持不同的主题
HandlerMapping
、HandlerAdapter
和ViewResolver
,因为这是阅读源码时,避不开的三个组件。调试准备
Spring web
项目即可Controller
部分@Controllerpublic class IndexController {
@RequestMapping('/index/home') public String home(String id, Student student, @RequestParam('code') String code) { System.out.println(student.getName()); return 'index'; }
@ResponseBody @RequestMapping('/index/list') public String list() { return 'success'; }}
Entity
部分
public class Student {
private String name;
private Integer gender;
// getter、setter
}
Spring
源码非常庞大,不能只见树木不见森林,需要有针对性的阅读,所以本篇只需要关注主体流程即可。核心方法
SpringMVC
有一个用来分发请求的前端控制器DispatcherServlet
,其中用来处理请求的方法就是doService
,该方法定义如下/** * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} * for the actual dispatching. */@Overrideprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request);
// Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } }
// Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); }
try { // 真正执行的方法 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }}
doDispatch
doDispatch
是doService
中真正用来处理请求的方法/**
* 实际处理请求的方法
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 校验是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 为当前请求找到一个合适的处理器(Handler)
// 返回值是一个HandlerExecutionChain,也就是处理器执行链
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理GET请求的缓存
String method = request.getMethod();
boolean isGet = 'GET'.equals(method);
if (isGet || 'HEAD'.equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 执行拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 利用HandlerAdapter来执行Handler里对应的处理方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果没有设置视图,则应用默认的视图名
applyDefaultViewName(processedRequest, mv);
// 执行拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException('Handler dispatch failed', err);
}
// 根据ModelAndView对象解析视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException('Handler processing failed', err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
SpringMVC
处理请求的整体流程,其中涉及到几个重要的方法。getHandler
/** * Return the HandlerExecutionChain for this request. * 为这个request返回一个HandlerExecutionChain */@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null;}
getHandler
方法主要是从List<HandlerMapping> handlerMappings
集合中遍历查找一个合适的处理器(Handler
),返回的结果是一个HandlerExecutionChain
。然后再根据HandlerExecutionChain
里携带的Handler
去获取HandlerAdapter
。getHandlerAdapter
getHandlerAdapter
方法定义如下/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException('No adapter for handler [' + handler +
']: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler');
}
getHandlerAdapter
方法主要是从List<HandlerAdapter> handlerAdapters
集合中遍历查找一个合适的处理器适配器(HandlerAdapter
),返回的结果是一个HandlerAdapter
。HandlerAdapter
真正的实现类是RequestMappingHandlerAdapter
。processDispatchResult
processDispatchResult
方法主要根据方法执行完成后封装的ModelAndView
,转发到对应页面,定义如下/** * Handle the result of handler selection and handler invocation, which is * either a ModelAndView or an Exception to be resolved to a ModelAndView. */private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug('ModelAndViewDefiningException encountered', exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } }
// Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { // 主要调用该方法渲染视图 render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace('No view rendering, null ModelAndView returned.'); } }
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; }
if (mappedHandler != null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); }}
render
render
方法定义如下/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 根据给定的视图名称,解析获取View对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException('Could not resolve view with name '' + mv.getViewName() +
'' in servlet with name '' + getServletName() + ''');
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException('ModelAndView [' + mv + '] neither contains a view name nor a ' +
'View object in servlet with name '' + getServletName() + ''');
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace('Rendering view [' + view + '] ');
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug('Error rendering view [' + view + ']', ex);
}
throw ex;
}
}
resolveViewName
resolveViewName
方法定义如下
@Nullableprotected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null;}
ViewResolver
的是InternalResourceViewResolver
类,也就是我们经常配置的一项类型<!-- 定义视图文件解析 -->
<bean class='org.springframework.web.servlet.view.InternalResourceViewResolver'>
<property name='prefix' value='/WEB-INF/views/' />
<property name='suffix' value='.html' />
</bean>
SpringMVC
处理请求的完整逻辑SpringMVC
处理请求的整个流程已经梳理清楚了。> SpringMVC处理请求完成后,一定会返回
ModelAndView
吗,如果加了@ResponseBody
注解呢?参数绑定
handler
的方法,参数的绑定和返回值的处理都在这个方法里,也就是// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handle
handle
方法的作用是根据请求参数,执行真正的处理方法,并且返回合适的ModelAndView
对象,也有可能返回null
。该方法定义如下在
AbstractHandlerMethodAdapter
类中/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
handleInternal
handleInternal
方法@Overrideprotected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav; // 校验指定的请求以获取受支持的方法类型(GET、POST等)和所需的session checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... // 真正执行handler的方法 mav = invokeHandlerMethod(request, response, handlerMethod); }
if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } }
return mav;}
invokeHandlerMethod
invokeHandlerMethod
方法/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* 执行@RequestMapping标注的handler方法,如果需要解析视图就准备一个ModelAndView
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// HandlerMethod接口封装执行方法的信息,提供对方法参数,方法返回值,方法注释等的便捷访问。
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndViewContainer可以看做ModelAndView的上下文容器,关联着Model和View的信息
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return 'Resume with async result [' + formatted + ']';
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 真正执行Handler的方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 获取ModelAndeView对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
invokeAndHandle
方法的作用是执行并处理真正响应请求的方法,该方法定义如下/** * Invoke the method and handle the return value through one of the * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}. * @param webRequest the current request * @param mavContainer the ModelAndViewContainer for this request * @param providedArgs 'given' arguments matched by type (not resolved) */public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 执行handler的方法 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest);
if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; }
mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, 'No return value handlers'); try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; }}
invokeForRequest
/**
* Invoke the method after resolving its argument values in the context of the given request.
* <p>Argument values are commonly resolved through
* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
* The {@code providedArgs} parameter however may supply argument values to be used directly,
* i.e. without argument resolution. Examples of provided argument values include a
* {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
* Provided argument values are checked before argument resolvers.
* <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
* resolved arguments.
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs 'given' arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @throws Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
* @see #getMethodArgumentValues
* @see #doInvoke
*/
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace('Arguments: ' + Arrays.toString(args));
}
// 执行
return doInvoke(args);
}
invoke
,所以更重要的是参数是如何绑定的,详情就在getMethodArgumentValues
方法getMethodArgumentValues
getMethodArgumentValues
方法用于从request
请求中获取真正的参数,返回的是Object
数组,该方法定义如下/** * Get the method argument values for the current request, checking the provided * argument values and falling back to the configured argument resolvers. * <p>The resulting array will be passed into {@link #doInvoke}. * @since 5.1.2 */protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 获取方法上所有的参数 MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; }
Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, 'No suitable resolver')); } try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args;}
HandlerMethodArgumentResolver
接口的实现类HandlerMethodArgumentResolverComposite
,此时正在处理的参数是一个Student
对象,并且已经把值注绑定了,也就是说真正执行绑定的是方法resolveArgument
resolveArgument
resolveArgument
是真正执行绑定的的方法/**
* Iterate over registered
* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}
* and invoke the one that supports it.
* @throws IllegalArgumentException if no suitable argument resolver is found
*/
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取合适的参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException('Unsupported parameter type [' +
parameter.getParameterType().getName() + ']. supportsParameter should be called first.');
}
// 执行参数绑定
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
getArgumentResolver
getArgumentResolver
该方法用于执行参数的绑定,定义如下/** * Find a registered {@link HandlerMethodArgumentResolver} that supports * the given method parameter. */@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result;}
argumentResolver
缓存中找到能够执行参数绑定的HandlerMethodArgumentResolver
,如果找不到就从HandlerMethodArgumentResolver
找,SpringMVC
支持的HandlerMethodArgumentResolver
一共有26种,用来解析各种类型的参数RequestParamMethodArgumentResolver
:处理普通参数(基本类型、包装类型、String
),不管加不加@RequestParam
注解ServletModelAttributeMethodProcessor
:处理POJO类型的参数,比如自定义的Student
对象RequestResponseBodyMethodProcessor
:处理@RequestBody
注解类型的参数
resolveArgument
HandlerMethodArgumentResolver
来处理,此处选取POJO类型参数的注入实现,对应的参数解析类是ModelAttributeMethodProcessor
,其中resolveArgument
方法用来解析(绑定)参数方法定义如下/**
* Resolve the argument from the model or if not found instantiate it with
* its default if it is available. The model attribute is then populated
* with request values via data binding and optionally validated
* if {@code @java.validation.Valid} is present on the argument.
* @throws BindException if data binding and validation result in an error
* and the next method parameter is not of type {@link Errors}
* @throws Exception if WebDataBinder initialization fails
*/
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, 'ModelAttributeMethodProcessor requires ModelAndViewContainer');
Assert.state(binderFactory != null, 'ModelAttributeMethodProcessor requires WebDataBinderFactory');
// 获取参数名
String name = ModelFactory.getNameForParameter(parameter);
// 获取参数上的ModelAttribute注解
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// Create attribute instance
try {
// 创建参数类型的实例(未注入值),底层就是通过反射调用构造方法
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
// 真正执行绑定(值注入)的方法
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
bindRequestParameters(binder, webRequest)
执行完成之后,POJO类型的参数已经完成了绑定。bindRequestParameters
/** * This implementation downcasts {@link WebDataBinder} to * {@link ServletRequestDataBinder} before binding. * @see ServletRequestDataBinderFactory */@Overrideprotected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) { ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class); Assert.state(servletRequest != null, 'No ServletRequest'); ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; // 执行绑定的方法 servletBinder.bind(servletRequest);}
bind
bind
方法public void bind(ServletRequest request) {
// 获取所有参数的键值对
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
// 处理文件上传请求
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
// 把url中携带的参数也加入到MutablePropertyValues
addBindValues(mpvs, request);
// 执行绑定(注入值)
doBind(mpvs);
}
由于调用层次过深,所以无法一步步列出下面的步骤,doBind
方法的原理还是通过调用POJO对象里的setter
方法设置值,可以查看最终的调试信息
setter
方法,具体执行的类是BeanWrapperImpl
。返回值处理
invokeAndHandle
invokeAndHandle
方法处(ServletInvocableHandlerMethod
类中),该方法定义如下/** * Invoke the method and handle the return value through one of the * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}. * @param webRequest the current request * @param mavContainer the ModelAndViewContainer for this request * @param providedArgs 'given' arguments matched by type (not resolved) */public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest);
if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; }
mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, 'No return value handlers'); try { // 真正处理不同类型返回值的方法 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; }}
handleReturnValue
方法handleReturnValue
/**
* Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 根据返回值个返回值类型选取合适的HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException('Unknown return value type: ' + returnType.getParameterType().getName());
}
// 真正的处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
selectHandler
@Nullableprivate HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null;}
SpringMVC
为返回值提供了15个HandlerMethodReturnValueHandler
的实现了来处理不同类型的返回值。@ResponseBody
类型的是RequestResponseBodyMethodProcessor
。@RequestBody
类型参数绑定也是用的这个类。RequestResponseBodyMethodProcessor
类的handleReturnValue
方法handleReturnValue
RequestResponseBodyMethodProcessor
类的handleReturnValue
方法定义如下这里设置了一个非常重要的属性 requestHandled
,这个属性关系到是否需要返回ModelAndView
对象
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 设置该请求是否已在处理程序中完全处理,例如@ResponseBody方法不需要视图解析器,此处就可以设置为true。
// 当控制器方法声明类型为ServletResponse或OutputStream的参数时,也可以设置此标志为true。
// 这个属性设置成true之后,上层getModelAndView获取ModelAndView时会返回Null,因为不需要视图。
// 默认值为false
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 底层就是利用java.io.OutputStreamWriter类把返回值写到网络IO
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
writeWithMessageConverters
方法,一步步调试到最后,底层就是利用java.io.OutputStreamWriter
类把返回值写到网络IOhandleReturnValue
把requestHandled
设置成了true
,上层在调用getModelAndView
方法时会返回null
,表示该请求不需要视图。感兴趣的同学自己调试一下便知。总结
SpringMVC
处理请求的整个流程,并且讲解了参数的绑定以及返回值的处理。相信大家看完后,结合自己的调试信息,会对SpringMVC
的请求处理过程有一个更深入的理解。