??@ModelAttribute
应用请参照《Spring 注解面面通 之 @ModelAttribute 模型属性绑定全解》。
??@ModelAttribute
可以注释方法和参数,那么源码分析也从这两个方向进行分析。
??源码流程
??流程图中,左侧为@ModelAttribute
注释方法的处理过程,右侧为@ModelAttribute
注释参数的处理过程。
??需要说明的是,流程图中,由于整个过程比较复杂,图中只是简单绘制与@ModelAttribute
注解处理相关的内容,其他部分未列其内。此图并不代表完整的流程。
??注意:@ModelAttribute
注释方法解析逻辑主要集中在ModelFactory.invokeModelAttributeMethods(...)
,@ModelAttribute
注释方法参数解析主要集中在ModelAttributeMethodProcessor.resolveArgument(...)
。
??源码解析过程涉及其他类,主要是为了说明Spring MVC整个流程的处理逻辑,避免对@ModelAttribute
解析部分理解出现偏差。
??源码解析
??1) DispatcherServlet.doDispatch(...)
。
??① 取得请求HttpServletRequest
异步管理器WebAsyncManager
。
??② 确认请求HttpServletRequest
是否为multipart/form-data
类型请求。
??③ 通过请求HttpServletRequest
从DispatcherServlet.properties
提供的默认策略中获取org.springframework.web.servlet.HandlerMapping
列表,通过其取得对应的处理器。
??④ 通过请求HttpServletRequest
从DispatcherServlet.properties
提供的默认策略中获取org.springframework.web.servlet.HandlerAdapter
列表,通过其取得③
中处理器对应的处理器适配器。
??⑤ 当Http方法为GET
、HEAD
时,处理Last-Modified
请求标头。
??⑥ 按照执行链上拦截器的顺序调用其preHandle(...)
方法,此时,若preHandle(...)
返回false
,会按照执行链上拦截器的反顺序调用其afterCompletion(...)
方法。
???⑦ 调用处理器的handle(...)
进行实际处理。
??⑧ 若是异步请求,则异步进行处理器处理,DispatcherServlet.doDispatch(...)
返回。
??⑨ 若⑦
中返回的ModelAndView不包括视图,则配置默认视图。
??⑩ 按照执行链上拦截器的反顺序调用其postHandle(...)
方法。
??? 调用DispatcherServlet.processDispatchResult(...)
方法处理最终结果,并在方法最后按照执行链上拦截器的反顺序调用其afterCompletion(...)
方法。
??? 此外,在最外层try { ... } catch (Exception ex) { ... } catch (Throwable err) { ... }
的catch { ... }
中也会后按照执行链上拦截器的反顺序调用其afterCompletion(...)
方法,但仔细查看代码会发现,这种情况发生概率几乎为零。
??DispatcherServlet.doDispatch(...)
源码注释:
/*** 分发请求给处理程序.* 处理程序将按照顺序处理Servlet的HandlerMappings.* HandlerAdapter将通过查询Servlet已配置的HandlerAdapter来获取,以找到第一个支持handler类的HandlerAdapter.* 所有的HTTP方法都由这个方法处理.* 由HandlerAdapter或处理程序自己决定哪些方法是可接受的.* @param request 当前请求实例.* @param response 当前响应实例.* @throws Exception .*/
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 {
// 确认是否multipart/form-data请求.processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 获得当前请求的处理器.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {
noHandlerFound(processedRequest, response);return;}// 获取当前请求的处理器适配器.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 处理last-modified请求头.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;}}// 调用拦截器的applyPreHandle方法.if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;}// 实际调用处理器方法处理请求.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {
return;}// 处理默认视图.applyDefaultViewName(processedRequest, mv);// 调用拦截器的applyPostHandle方法.mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {
dispatchException = ex;}catch (Throwable err) {
// 从4.3开始,同时会处理从handler方法抛出的错误,使它们可以用于@ExceptionHandler方法和其他场景.dispatchException = new NestedServletException("Handler dispatch failed", err);}// 处理转发结果.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {
// 调用拦截器的triggerAfterCompletion方法.triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {
// 调用拦截器的triggerAfterCompletion方法.triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// 代替postHandle和afterCompletion.if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {
// 清除multipart/form-data请求使用的所有资源.if (multipartRequestParsed) {
cleanupMultipart(processedRequest);}}}
}
??2) RequestMappingHandlerAdapter.handleInternal(...)
。
??① 检查给定请求,其包含的Http方法是否支持、若Session
设置为必需(requireSession
标志),其是否包含可用Session
。
???② 若Session
设置为需线程同步(synchronizeOnSession
)调用且Session
可用时,使用synchronized
取得重入锁之后,调用invokeHandlerMethod(...)
。
??③ 若Session
设置为需线程同步(synchronizeOnSession
)调用且Session
不可用时,无需使用synchronized
,直接调用invokeHandlerMethod(...)
。
??④ 若Session
未设置为需线程同步(synchronizeOnSession
)时,无需使用synchronized
,直接调用invokeHandlerMethod(...)
。
??⑤ 若响应不包含Cache-Control
标头,则对其进行处理。
/*** 内部处理方法.*/
@Override
protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;// 检查给定的请求以获得支持的方法和所需的会话(如果有).checkRequest(request);// 如果需要,在synchronized块中执行invokeHandlerMethod.if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);// 有HttpSession可用->需要互斥.if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);}}else {
// 没有HttpSession可用->不需要互斥.mav = invokeHandlerMethod(request, response, handlerMethod);}}else {
// 根本不需要会话同步.mav = invokeHandlerMethod(request, response, handlerMethod);}// 响应头中不包含Cache-Control头.进行缓存处理.if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {
prepareResponse(response);}}return mav;
}
??3) RequestMappingHandlerAdapter.invokeHandlerMethod(...)
。
???① 使用ServletWebRequest
包装HttpServletRequest
请求实例。
??② 获取WebDataBinderFactory
实例,主要目的是搜索由@InitBinder
注释的方法。
??③ 获取ModelFactory
实例,主要目的是搜索由@ModelAttribute
注释,且无@RequestMapping
注释的方法。
??④ 从给定的HandlerMethod定义创建ServletInvocableHandlerMethod。
??⑤ 设置默认初始化的参数解析器。
??⑥ 设置默认初始化的返回值处理器。
??⑦ 创建ModelAndViewContainer
实例,用于装载模型数据和视图信息。
??⑧ 调用ModelFactory.initModel(...)
进行模型数据初始化。
??⑨ 对异步请求操作进行处理。
??⑩ 调用实际处理器方法。
???? 从webRequest
和modelFactory
中获取Model
和View
设置到ModelAndView
中。
/*** 如果需要视图解析,则调用RequestMapping处理程序方法准备ModelAndView.*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 包装请求实例.ServletWebRequest webRequest = new ServletWebRequest(request, response);try {
// 获取WebDataBinderFactory实例.WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);// 获取ModelFactory实例.ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// 从给定的HandlerMethod定义创建ServletInvocableHandlerMethod.ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 设置参数解析器.if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 设置返回值解析器.if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}// 设置WebDataBinderFactory.invocableMethod.setDataBinderFactory(binderFactory);// 设置参数名称发现器.invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// ModelAndView容器.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();if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");}// ServletInvocableHandlerMethod包装.invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 调用和处理请求.invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {
return null;}// 获取ModelAndView.return getModelAndView(mavContainer, modelFactory, webRequest);}finally {
webRequest.requestCompleted();}
}
??4) ModelFactory.invokeModelAttributeMethods(...)
。
??注意: 此方法是处理@ModelAttribute
注释方法的重点方法,完成了方法调用,且将处理过的键值对设置到ModelMap
中。
??① 判断是否存在符合条件的方法,由@ModelAttribute
注释,且无@RequestMapping
注释的方法。
??② 若存在符合条件的方法,则循环遍历方法列表进行处理。
??③ 若当前Controller
的ModelAndView
已包含@ModelAttribute
注解的name
属性值,且@ModelAttribute
注解的binding
值为false
时,跳过当前遍历。
??④ 调用@ModelAttribute
注释的方法,获取其返回值。
???⑤ 若返回值类型不为void
,则通过getNameForReturnValue(...)
方法获取值作为键,④
中返回值作为值,设置到ModelAndViewContainer
的ModelMap
中。
/*** 调用Model属性方法来填充Model.* 仅当模型中不存在属性时才添加属性.*/
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)throws Exception {
// 存在@ModelAttribute注释的方法.while (!this.modelMethods.isEmpty()) {
// 获取下一个待处理的ModelMethod.InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();// 取得ModelMethod的@ModelAttribute注解.ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);Assert.state(ann != null, "No ModelAttribute annotation");// 若当前Controller的ModelAndView已包含@ModelAttribute注解的name属性值,// 且@ModelAttribute注解的binding值为false时,跳过当前遍历.if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());}continue;}// 调用@ModelAttribute注释的方法.Object returnValue = modelMethod.invokeForRequest(request, container);// @ModelAttribute注释的方法返回值类型不是void类型.if (!modelMethod.isVoid()){
// 获取返回值的名称.String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());// 若注解@ModelAttribute的binding属性为false,设置ModelAndViewContainer// 的bindingDsiabled属性为returnValueName.if (!ann.binding()) {
container.setBindingDisabled(returnValueName);}// 若当前Controller的ModelAndView不包含returnValueName,则设置到Controller的ModelAndView中.if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);}}}
}
??5) ServletInvocableHandlerMethod.invokeAndHandle(...)
。
??① 调用处理方法请求并获取返回值。
??② 设置请求的响应状态。
??③ 处理@ResponseStatus
注解,并映射到响应状态。
??④ 调用HandlerMethodReturnValueHandlerComposite
处理返回值。
/*** 调用方法并通过配置的HandlerMethodReturnValueHandler处理返回值.* @param webRequest 当前请求实例.* @param mavContainer 请求的ModelAndViewContainer.* @param providedArgs 与类型匹配的给定参数(未解析).*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
// 调用方法请求并获取返回值.Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 设置响应状态.setResponseStatus(webRequest);// 返回值为空.if (returnValue == null) {
// 1.请求是否满足"未被修改"条件.// 2.@ResponseStatus设置的响应状态代码不为空.// 3.请求是否已在处理程序中完全处理.// 满足以上三个条件,设置请求已处理完成,直接返回.if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);return;}}// @ResponseStatus设置的响应状态原因不为空,设置请求已处理完成,直接返回.else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);return;}// 设置请求未处理完成.mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {
// 调用HandlerMethodReturnValueHandlerComposite处理返回值.this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);}throw ex;}
}
??6) HandlerMethodArgumentResolverComposite.getArgumentResolver(...)
。
??① 从缓存中获取默认初始化的HandlerMethodArgumentResolver
。
??② 遍历argumentResolvers
,找到适合当前MethodParameter
的HandlerMethodArgumentResolver
。
??③ 调用具体HandlerMethodArgumentResolver
进行参数解析处理。
/*** 查找支持给MethodParameter的解析程序.*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// 从缓存中获取HandlerMethodArgumentResolver.HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {
// 遍历argumentResolvers,找到适合当前MethodParameter的HandlerMethodArgumentResolver.for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +parameter.getGenericParameterType() + "]");}// 调用具体的解析程序处理.if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;
}
??7) ModelAttributeMethodProcessor.resolveArgument(...)
。
??① 当参数被@ModelAttribute
注释或annotationNotRequired
为true
且参数非基本类型时才会调用此方法参数解析器。
???② 直接根据@ModelAttribute
注解的value
属性值或参数名,从ModelAndViewContainer
的ModelMap
中取值。
??③ 进行参数绑定和校验,即处理@InitBinder
注解。
???④ 将经过绑定和校验的参数值添加到ModelAndViewContainer
的ModelMap
中。
/*** 从Model模型中解析参数,如果找不到参数,则使用其默认值(如果可用)将其实例化. * 然后通过数据绑定用请求值填充Model属性,如果参数上存在@java.validation.Valid,* 则可以选择验证该属性.*/
@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");// 根据@ModelAttribute参数注释(如果存在)// 或根据参数类型确定给定方法参数的模型属性名称.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;// ModelAndViewContainer包含此名称,则从ModelAndViewContainer中取得属性的值.if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);}else {
// 创建属性实例.try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindExceptionthrow ex;}// Otherwise, expose null/empty value and associated BindingResultif (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();}bindingResult = ex.getBindingResult();}}// 绑定过程无异常.if (bindingResult == null) {
// Bean属性绑定和验证,绑定失败时跳过.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.Optionalif (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult = binder.getBindingResult();}// 在模型末尾添加解析属性和BindingResult.Map<String, Object> bindingResultModel = bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;
}
??总结
??整体的流程还是比较复杂的,原因在于所有的处理都掺杂在主流程的处理过程中,这使得解析过程看起来有些繁杂。
???源码解析基于spring-framework-5.0.5.RELEASE
版本源码。
??若文中存在错误和不足,欢迎指正!