本文主要是介绍SpringMVC源码学习(七)---解析ModelAndView,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
解析ModelAndView
- 一. RequestMappingHandlerAdapter类invokeHandlerMethod()方法
- 二. invokeAndHandle() 方法
- 三. handleReturnValue()方法
- 四. ModelAndViewMethodReturnValueHandler类中的handleReturnValue() 方法
- 五. RequestMappingHandlerAdapter类中的getModelAndView()方法
- 六. DispatcherServlet类中的processDispatchResult()方法
- 七. DispatcherServlet类中的processHandlerException()方法
- 八. DispatcherServlet类中的render()方法
- 九. ViewResolver和View接口
- 十. DispatcherServlet类中的resolveViewName()方法
- 十一. AbstractCachingViewResolver类中的resolveViewName()方法
- 十二. UrlBasedViewResolver类中的createView()方法
- 十三. UrlBasedViewResolver类中的loadView()方法
- 十四. UrlBasedViewResolver类中的buildView()方法
- 十五. UrlBasedViewResolver类中的applyLifecycleMethods()方法
- 十六. AbstractView类中的render()方法
- 十七. InternalResourceView类中的renderMergedOutputModel()方法
一. RequestMappingHandlerAdapter类invokeHandlerMethod()方法
在RequestMappingHandlerAdapter#invokeHandlerMethod()方法中
- 执行处理器方法(HandlerMethod)
- 封装并返回ModelAndView实例
@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//将Request和Response进行封装ServletWebRequest webRequest = new ServletWebRequest(request, response);try {//WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例// 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);// 获取ModelFactory:// 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// 创建InvocableHandlerMethod实例,以及各个组件的配置;// 后面通过调用invokeAndHandle()方法执行处理器ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 4.尝试绑定参数、返回值解析器if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 创建视图容器, 用于封装视图, 数据模型, 处理状态等信息ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);// 6.异步请求相关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 + "]");}invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 7.调用Controller中的具体方法并处理返回值 执行处理器invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}// 8.返回ModelAndView对象return getModelAndView(mavContainer, modelFactory, webRequest);}finally {// 完成请求后续处理,并将当前请求置为未激活webRequest.requestCompleted();}}
二. invokeAndHandle() 方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 1.调用Controller中的具体方法Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 2.设置返回状态码setResponseStatus(webRequest);// 3.当前请求无返回值或者返回值中包含错误,则将请求完成标识设置为true并返回if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}// 4.当前请求有返回值且无错误信息,则将请求完成标识设置为false,并继续处理当前请求mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {// 选取合适的HandlerMethodReturnValueHandler,并处理返回值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;}}
三. handleReturnValue()方法
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//根据返回值以及返回类型选择合适的返回值处理器, 对返回值进行解析HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}//解析结果handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}
四. ModelAndViewMethodReturnValueHandler类中的handleReturnValue() 方法
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 当前请求返回值为null,无需处理,并且要将当前请求标记已处理if (returnValue == null) {mavContainer.setRequestHandled(true);return;}// 处理引用视图ModelAndView mav = (ModelAndView) returnValue;if (mav.isReference()) {String viewName = mav.getViewName();mavContainer.setViewName(viewName);if (viewName != null && isRedirectViewName(viewName)) {mavContainer.setRedirectModelScenario(true);}}// 处理普通视图(即我们已经制定了具体的View视图,而无需通过视图解析器再次解析)else {View view = mav.getView();mavContainer.setView(view);if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {mavContainer.setRedirectModelScenario(true);}}// 设置返回状态mavContainer.setStatus(mav.getStatus());//设置数据ModelmavContainer.addAllAttributes(mav.getModel());}
五. RequestMappingHandlerAdapter类中的getModelAndView()方法
从ModelAndViewContainer容器中获取ModeAndView实例
@Nullableprivate ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {// 1.更新模型modelFactory.updateModel(webRequest, mavContainer);if (mavContainer.isRequestHandled()) {return null;}// 2.获取ModelMap并创建ModelAndViewModelMap model = mavContainer.getModel();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());// 3.处理引用类型视图和转发类型视图if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);if (request != null) {RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}}return mav;}
六. DispatcherServlet类中的processDispatchResult()方法
解析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);}}// 尝试解析视图和模型;// wasCleared:判断当前模型和视图是否已经被标识为清空,且当前视图和模型是否同时为空if (mv != null && !mv.wasCleared()) {// 解析并渲染视图render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isDebugEnabled()) {logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +"': assuming HandlerAdapter completed request handling");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {return;}// 处理注册的后置完成拦截器if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}
processDispatchResult() 方法
- 如果出现异常,则解析异常视图
- 解析ModelAndView
七. DispatcherServlet类中的processHandlerException()方法
@Nullableprotected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {//遍历所有的异常解析器, 尝试对异常进行解析, 如果解析成功,跳出循焕for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {if (exMv.isEmpty()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;}//对于简单的错误模型,我们可能仍需要视图名称转换if (!exMv.hasView()) {String defaultViewName = getDefaultViewName(request);if (defaultViewName != null) {exMv.setViewName(defaultViewName);}}if (logger.isDebugEnabled()) {logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);}//设置错误请求的相关属性WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());return exMv;}throw ex;}
八. DispatcherServlet类中的render()方法
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// 确定请求的区域设置并将其应用于响应。Locale locale =(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());response.setLocale(locale);View view;// 获取视图名String viewName = mv.getViewName();if (viewName != null) {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() + "'");}}// 获取到视图名,再次判断当前ModelAndView对象中是否包含真正的View对象,// 因为接下来需要调用View对象的render方法else {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.isDebugEnabled()) {logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");}try {// 设置返回状态码if (mv.getStatus() != null) {response.setStatus(mv.getStatus().value());}// 调用View对象的render方法完成视图解析view.render(mv.getModelInternal(), request, response);}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +getServletName() + "'", ex);}throw ex;}}
ModelAndView视图解析可以分为两个重要步骤:
- 根据视图名称创建View实例
- 根据View实例渲染视图
九. ViewResolver和View接口
public interface ViewResolver {// 通过逻辑视图名和用户地区信息生成View对象@NullableView resolveViewName(String viewName, Locale locale) throws Exception;}
public interface View {String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";String PATH_VARIABLES = View.class.getName() + ".pathVariables";String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";//获取返回值的contentType@Nullabledefault String getContentType() {return null;}//通过用户提供的模型数据与视图信息渲染视图void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception;}
十. DispatcherServlet类中的resolveViewName()方法
//根据视图名称创建View实例@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;}
十一. AbstractCachingViewResolver类中的resolveViewName()方法
public View resolveViewName(String viewName, Locale locale) throws Exception {//判断缓存是否可用if (!isCache()) {//如果缓存不可用, 则直接创建视图return createView(viewName, locale);}else {//如果缓存可用, 则先尝试从缓存中获取//生成缓存KeyObject cacheKey = getCacheKey(viewName, locale);//尝试从缓存中获取视图View view = this.viewAccessCache.get(cacheKey);if (view == null) {//如果从缓存中获取视图失败, 则尝试从viewCreationCache缓存中获取synchronized (this.viewCreationCache) {//让子类创建View对象, 留给子类扩展[扩展开放,修改关闭原则]view = this.viewCreationCache.get(cacheKey);if (view == null) {// 这里cacheUnresolved指的是是否缓存默认的空视图,UNRESOLVED_VIEW是// 一个没有任何内容的Viewview = createView(viewName, locale);if (view == null && this.cacheUnresolved) {view = UNRESOLVED_VIEW;}if (view != null) {//将创建的view视图加入缓存this.viewAccessCache.put(cacheKey, view);this.viewCreationCache.put(cacheKey, view);if (logger.isTraceEnabled()) {logger.trace("Cached view [" + cacheKey + "]");}}}}}return (view != UNRESOLVED_VIEW ? view : null);}}
十二. UrlBasedViewResolver类中的createView()方法
@Overrideprotected View createView(String viewName, Locale locale) throws Exception {if (!canHandle(viewName, locale)) {return null;}// 检查特殊的"redirect:"前缀 REDIRECT_URL_PREFIX = "redirect:"// 如果是以"redirect:" 开头, 说明该视图是重定向if (viewName.startsWith(REDIRECT_URL_PREFIX)) {String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());RedirectView view = new RedirectView(redirectUrl,isRedirectContextRelative(), isRedirectHttp10Compatible());String[] hosts = getRedirectHosts();if (hosts != null) {view.setHosts(hosts);}return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);}// 检查特殊的"forward:"前缀 FORWARD_URL_PREFIX = "forward:"// 如果是以"forward:" 开头, 说明该视图是请求转发if (viewName.startsWith(FORWARD_URL_PREFIX)) {String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());return new InternalResourceView(forwardUrl);}//如果是普通视图, 创建该视图视图return super.createView(viewName, locale);}
十三. UrlBasedViewResolver类中的loadView()方法
@Overrideprotected View loadView(String viewName, Locale locale) throws Exception {// 使用逻辑视图名按照指定规则生成View对象AbstractUrlBasedView view = buildView(viewName);// 应用声明周期函数,也就是调用View对象的初始化函数和Spring用于切入bean创建的View result = applyLifecycleMethods(viewName, view);// 检查view的准确性,这里默认始终返回truereturn (view.checkResource(locale) ? result : null);}
十四. UrlBasedViewResolver类中的buildView()方法
protected AbstractUrlBasedView buildView(String viewName) throws Exception {//对于InternalResourceViewResolver而言,其返回的View对象的具体类型是InternalResourceViewClass<?> viewClass = getViewClass();Assert.state(viewClass != null, "No view class");// 使用反射生成InternalResourceView对象实例AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);//根据前缀和后缀拼接视图路径信息view.setUrl(getPrefix() + viewName + getSuffix());// 设置View的contentType属性String contentType = getContentType();if (contentType != null) {view.setContentType(contentType);}// 设置contextAttribute和attributeMap等属性view.setRequestContextAttribute(getRequestContextAttribute());view.setAttributesMap(getAttributesMap());// pathVariables表示request请求url中的属性,这里主要是设置是否将这些属性暴露到视图中Boolean exposePathVariables = getExposePathVariables();if (exposePathVariables != null) {view.setExposePathVariables(exposePathVariables);}// 这里设置的是是否将Spring的bean暴露在视图中,以供给前端调用Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();if (exposeContextBeansAsAttributes != null) {view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);}// 设置需要暴露给前端页面的bean名称String[] exposedContextBeanNames = getExposedContextBeanNames();if (exposedContextBeanNames != null) {view.setExposedContextBeanNames(exposedContextBeanNames);}return view;}
十五. UrlBasedViewResolver类中的applyLifecycleMethods()方法
十六. AbstractView类中的render()方法
protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {ApplicationContext context = getApplicationContext();if (context != null) {// 对生成的View对象应用初始化方法,主要包括InitializingBean.afterProperties()和一些// Processor,Aware方法Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);if (initialized instanceof View) {return (View) initialized;}}return view;}
@Overridepublic void render(@Nullable Map<String, ?> model, HttpServletRequest request,HttpServletResponse response) throws Exception {if (logger.isTraceEnabled()) {logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +" and static attributes " + this.staticAttributes);}// 合并为一个Map对象,以供给后面对视图的渲染使用Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);// 判断当前View对象的类型是否为文件下载类型,如果是文件下载类型,则设置response的prepareResponse(request, response);// 开始view视图渲染以及数据输出整理 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}
十七. InternalResourceView类中的renderMergedOutputModel()方法
@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// 将Model中的键值对数据全部写进RequestScope中exposeModelAsRequestAttributes(model, request);// 提供的一个hook方法,默认是空实现,用于用户进行request属性的自定义使用exposeHelpers(request);//确定请求分配器的路径String dispatcherPath = prepareForRendering(request, response);// 获取可应用于 forward/include 的RequestDispatcherRequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// 判断当前是否为include请求,如果是,则调用RequestDispatcher.include()方法进行文件引入if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");}rd.include(request, response);}// 请求转发//使用forward跳转则后面的response输出则不会执行,而用include来跳转,//则include的servlet执行完后,再返回到原来的servlet执行response的输出(如果有)else {if (logger.isDebugEnabled()) {logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");}//如果当前不是include()请求,则直接使用forward请求将当前请求转发到目标文件路径中,从而渲染该视图rd.forward(request, response);}}
视图解析分析完成
这篇关于SpringMVC源码学习(七)---解析ModelAndView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!