热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

spring源码_spring源码分析MVC

前期回顾spring源码分析-IOCspring源码分析-DIspring源码分析-AOP在springBoot之前,我们再创建springweb工程的时候ÿ

前期回顾

spring源码分析-IOC

spring源码分析-DI

spring源码分析-AOP

在spring Boot之前,我们再创建spring web工程的时候,都会创建非常多的配置,下面我们来看一下web.xml中配置的一段代码

dispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath*:spring-mvc.xml1dispatcherServlet/*

这个是我们启动spring web的一段关键代码,以前,web项目都是基于servlet规范的,而servlet在初始化的会调用init方法,这个servlet就是启动web项目的入口,下面我们来看一下DispatcherServlet类.在DispatcherServlet类中,我们找不到init方法,因此,寻找其父类。

在HttpServletBean类中,我们找到了init方法

public final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. initServletBean();}

在这段代码这,我们看到了一个关键的方法initServletBean,调用这个方法开始初始化servlet,我们来分析一下这个方法

protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); }}

看到这里,我们看见了一个关键的方法,初始化web上下文容器initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac;}

configureAndRefreshWebApplicationContext方法帮助我们配置和刷新上下文容器,他帮助我们完成了配置文件中bean的加载,通过这个方法,就可以跟我们之前启动的spring的方式关联上了

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); wac.refresh();}

在这里我们看到了一个令人惊喜的方法,refresh,这个就是spring上下文容器容器启动的方法 ,他最终会调用AbstractApplicationContext的refresh方法,这个方法终于将spring,spring mvc的启动关联起来了

我们再接着来分析mvc的启动过程,在启动完spring容器后,它会调用onRefresh方法来启动spring的九大组件

protected void onRefresh(ApplicationContext context) { initStrategies(context);}/** * Initialize the strategy objects that this servlet uses. *

May be overridden in subclasses in order to initialize further strategy objects. */protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context);}

看到这里,我们看到了平时非常熟悉的几大组件MultipartResolver(多文件上传)、HandlerMappings、HandlerAdapters(将 controller返回的方法适配成一个ModelAndView)、HandlerExceptionResolvers(异常处理),RequestToViewNameTranslator,ViewResolvers(将结果处理成一个view),FlashMapManager(进行redirect参数传输)

下面,我们来看一下这些组件是如何进行初始化的

private void initMultipartResolver(ApplicationContext context) { try { this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.multipartResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isTraceEnabled()) { logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared"); } }}

通过这段代码,我们可以看到,组件的初始化比较简单this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);,就是取出容器中的bean赋值给这些组件,这些组件的初始化大致相同,我就不一一分析了,有兴趣的可以自己看看

下面我们来分析一下调用过程是怎样的

熟悉servlet规范的都知道,在调用servlet时,会调用servlet的service方法,在寻找方法时,我们优先在当前类中进行查找,即先查找DispatcherServlet,然后再在其父类中依次寻找,通过这种方式,我们在FrameworkServlet类中找到了service方法

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); // 当请求方式为PATCH或者为空时,调用`processRequest` if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { // 调用父类的service方法 super.service(request, response); }}

一般我们的请求方式为GET或者POST,因此调用super.service(request, response);,已GET请求为例

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } if (ifModifiedSince

由于doGet方法被子类覆盖(Override)了,因此,这里会调用子类的doGet方法,因此,有回到了FrameworkServlet类中

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}

在这里有调用了processRequest,到这里,又和上面其他的请求类型殊途同归了

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); }}

到这里,我们又看到了一个关键的方法,doService,我们来看一下这个方法具体做了什么,这个方法被DispatcherServlet重写了,因此,我们直接看DispatcherServlet类中的doService方法

protected 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 attributesSnapshot &#61; null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot &#61; new HashMap<>(); Enumeration> attrNames &#61; request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName &#61; (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 !&#61; null) { // 获取redirect传输的参数 FlashMap inputFlashMap &#61; this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap !&#61; 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 !&#61; null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }}

我们又看到了一个干活的方法doDispatch(request, response);

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest &#61; request; HandlerExecutionChain mappedHandler &#61; null; boolean multipartRequestParsed &#61; false; WebAsyncManager asyncManager &#61; WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv &#61; null; Exception dispatchException &#61; null; try { // 多文件上传 processedRequest &#61; checkMultipart(request); multipartRequestParsed &#61; (processedRequest !&#61; request); // Determine handler for the current request. // 获取handler mappedHandler &#61; getHandler(processedRequest); if (mappedHandler &#61;&#61; null) { // 找不到handler&#xff0c;返回404 noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. // 获取HandlerAdapter HandlerAdapter ha &#61; getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method &#61; request.getMethod(); boolean isGet &#61; "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified &#61; 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将请求转成ModelAndView mv &#61; ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); // 调用拦截器的PostHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException &#61; ex; } catch (Throwable err) { // As of 4.3, we&#39;re processing Errors thrown from handler methods as well, // making them available for &#64;ExceptionHandler methods and other scenarios. dispatchException &#61; new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { // 调用拦截器的AfterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { // 调用拦截器的AfterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler !&#61; null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }}

通过这个方法&#xff0c;我们找到了在web中我们常用的HandlerInterceptor是如何触发的&#xff0c;也算是不虚此行,接下来&#xff0c;我们来分析一下HandlerAdapter如何将请求转化成ModelAndView的&#xff0c;即分析handle方法

5128f0a84b472db5073ece6818352659.png

在这里我们有看到了多个实现&#xff0c;有时候&#xff0c;我们无法知道具体调用的是那个类&#xff0c;这个时候&#xff0c;我们就可以采用调试的方式来判断具体调用的是那个类&#xff0c;AbstractHandlerMethodAdapter

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler);}

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session &#61; request.getSession(false); if (session !&#61; null) { Object mutex &#61; WebUtils.getSessionMutex(session); synchronized (mutex) { mav &#61; invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav &#61; invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav &#61; invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav;}

在这里有调用了invokeHandlerMethod&#xff0c;这个就是去通过反射调用controller中的方法,我们来具体看一下是怎么调用的

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest &#61; new ServletWebRequest(request, response); try { // 省略。。。 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); }}

我们接着看invokeAndHandle方法

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 执行请求 Object returnValue &#61; invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue &#61;&#61; null) { if (isRequestNotModified(webRequest) || getResponseStatus() !&#61; 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 !&#61; 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方法来获取returnValue

public Object invokeForRequest(NativeWebRequest request, &#64;Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args &#61; this.getMethodArgumentValues(request, mavContainer, providedArgs); if (this.logger.isTraceEnabled()) { this.logger.trace("Arguments: " &#43; Arrays.toString(args)); } return this.doInvoke(args);}

我们又看到了以do开头的方法,

protected Object doInvoke(Object... args) throws Exception { ReflectionUtils.makeAccessible(this.getBridgedMethod()); try { // 通过反射调用方法 return this.getBridgedMethod().invoke(this.getBean(), args); } catch (IllegalArgumentException var4) { this.assertTargetBean(this.getBridgedMethod(), this.getBean(), args); String text &#61; var4.getMessage() !&#61; null ? var4.getMessage() : "Illegal argument"; throw new IllegalStateException(this.formatInvokeError(text, args), var4); } catch (InvocationTargetException var5) { Throwable targetException &#61; var5.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException)targetException; } else if (targetException instanceof Error) { throw (Error)targetException; } else if (targetException instanceof Exception) { throw (Exception)targetException; } else { throw new IllegalStateException(this.formatInvokeError("Invocation failure", args), targetException); } }}

我们终于看到了反射调用method的方法

接下来&#xff0c;我们接着分析spring如何将返回值处理成ModelAndView&#xff0c;这个也十分的简单,就是调用了getModelAndView,方法&#xff0c;然后通过new来创建一个ModelAndView

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model &#61; mavContainer.getModel(); ModelAndView mav &#61; new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map flashAttributes &#61; ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request &#61; webRequest.getNativeRequest(HttpServletRequest.class); if (request !&#61; null) { RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav;}

我们接着来看spring如何处理ModelAndView&#xff0c;这个方法就是processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, &#64;Nullable HandlerExecutionChain mappedHandler, &#64;Nullable ModelAndView mv, &#64;Nullable Exception exception) throws Exception { boolean errorView &#61; false; if (exception !&#61; null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv &#61; ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler &#61; (mappedHandler !&#61; null ? mappedHandler.getHandler() : null); mv &#61; processHandlerException(request, response, handler, exception); errorView &#61; (mv !&#61; null); } } // Did the handler return a view to render? if (mv !&#61; 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 !&#61; null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); }}

在这里&#xff0c;我们看到了两个关键的方法&#xff0c;一个是render(渲染页面&#xff0c;将ModelAndView转成view)&#xff0c;triggerAfterCompletion,调用拦截器的AfterCompletion方法,我们来分析一下render方法

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale &#61; (this.localeResolver !&#61; null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName &#61; mv.getViewName(); if (viewName !&#61; null) { // We need to resolve the view name. view &#61; resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view &#61;&#61; null) { throw new ServletException("Could not resolve view with name &#39;" &#43; mv.getViewName() &#43; "&#39; in servlet with name &#39;" &#43; getServletName() &#43; "&#39;"); } } else { // No need to lookup: the ModelAndView object contains the actual View object. view &#61; mv.getView(); if (view &#61;&#61; null) { throw new ServletException("ModelAndView [" &#43; mv &#43; "] neither contains a view name nor a " &#43; "View object in servlet with name &#39;" &#43; getServletName() &#43; "&#39;"); } } // Delegate to the View object for rendering. if (logger.isTraceEnabled()) { logger.trace("Rendering view [" &#43; view &#43; "] "); } try { if (mv.getStatus() !&#61; null) { response.setStatus(mv.getStatus().value()); } // 将view转成相应的页面 view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" &#43; view &#43; "]", ex); } throw ex; }}

我们接着来看view.render方法&#xff0c;这个有进入了AbstractView类中

public void render(&#64;Nullable Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { logger.debug("View " &#43; formatViewName() &#43; ", model " &#43; (model !&#61; null ? model : Collections.emptyMap()) &#43; (this.staticAttributes.isEmpty() ? "" : ", static attributes " &#43; this.staticAttributes)); } // 返回页面的参数 Map mergedModel &#61; createMergedOutputModel(model, request, response); prepareResponse(request, response); renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}

我们接着来看InternalResourceView的renderMergedOutputModel方法

protected void renderMergedOutputModel( Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, request); // Expose helpers as request attributes, if any. exposeHelpers(request); // Determine the path for the request dispatcher. String dispatcherPath &#61; prepareForRendering(request, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). RequestDispatcher rd &#61; getRequestDispatcher(request, dispatcherPath); if (rd &#61;&#61; null) { throw new ServletException("Could not get RequestDispatcher for [" &#43; getUrl() &#43; "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including [" &#43; getUrl() &#43; "]"); } rd.include(request, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. if (logger.isDebugEnabled()) { logger.debug("Forwarding to [" &#43; getUrl() &#43; "]"); } rd.forward(request, response); }}

下面附上spring MVC初始化、调用的时序图

95753c79d1f06707aabf71a49092a818.png



推荐阅读
author-avatar
睡姿决定发型
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有