Spring源码阅读(四):MVC实现原理

MVC实现原理

流程:
一个request由DispatcherServlet接收到,根据请求的url去handlerMapping取到对应的controller,调用controller的方法,返回ModelAndView,通过ViewResolver解析,得到View,最后渲染。

首先贴上时序图:

MVC的初始化阶段

DispatcherServlet继承了FrameworkServlet,FrameworkServlet继承了HttpServletBean,HttpServletBean里有一个init()方法:

@Override
public final void init() throws ServletException {
   if (logger.isDebugEnabled()) {
      logger.debug(""Initializing servlet '"" + getServletName() + ""'"");
   }

   // 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();

   if (logger.isDebugEnabled()) {
      logger.debug(""Servlet '"" + getServletName() + ""' configured successfully"");
   }
}

try...catch...部分主要获取了servlet的配置信息,直接来看来到initServletBean():

@Override
protected final void initServletBean() throws ServletException {
   getServletContext().log(""Initializing Spring FrameworkServlet '"" + getServletName() + ""'"");
   if (this.logger.isInfoEnabled()) {
      this.logger.info(""FrameworkServlet '"" + getServletName() + ""': initialization started"");
   }
   long startTime = System.currentTimeMillis();

   try {

      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet();
   }
   catch (ServletException ex) {
      this.logger.error(""Context initialization failed"", ex);
      throw ex;
   }
   catch (RuntimeException ex) {
      this.logger.error(""Context initialization failed"", ex);
      throw ex;
   }

   if (this.logger.isInfoEnabled()) {
      long elapsedTime = System.currentTimeMillis() - startTime;
      this.logger.info(""FrameworkServlet '"" + getServletName() + ""': initialization completed in "" +
            elapsedTime + "" ms"");
   }
}

其中initWebApplicationContext()

protected WebApplicationContext initWebApplicationContext() {

   //先从ServletContext中获得父容器WebAppliationContext
   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);
            }
            //这个方法里面调用了AbatractApplication的refresh()方法
            //模板方法,规定IOC初始化基本流程
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
   //先去ServletContext中查找Web容器的引用是否存在,并创建好默认的空IOC容器
   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();
   }
   //给上一步创建好的IOC容器赋值
   if (wac == null) {
      // No context instance is defined for this servlet -> create a local one
      wac = createWebApplicationContext(rootContext);
   }

   //触发onRefresh方法
   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.
      onRefresh(wac);
   }

   if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
      if (this.logger.isDebugEnabled()) {
         this.logger.debug(""Published WebApplicationContext of servlet '"" + getServletName() +
               ""' as ServletContext attribute with name ["" + attrName + ""]"");
      }
   }

   return wac;
}

这个方法里有一个configureAndRefreshWebApplicationContext()方法,进入这个方法,发现它调用了Ioc的refresh()方法。

WebApplicationContext和ApplicationContext区别:

  • WebApplicationContext存放的是web相关如Listener、Servlet、Filter、Session、Response、Page等,它继承了ApplicationContext,是ApplicationContext的扩展。

  • ApplicationContext 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。ApplicationContext 包含 BeanFactory 所有的功能。

进入下面的onRefresh(wac)方法:

protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

进入initStrategies(context):

//初始化策略
protected void initStrategies(ApplicationContext context) {
   //多文件上传的组件
   initMultipartResolver(context);
   //初始化本地语言环境
   initLocaleResolver(context);
   //初始化模板处理器
   initThemeResolver(context);
   //handlerMapping
   initHandlerMappings(context);
   //初始化参数适配器
   initHandlerAdapters(context);
   //初始化异常拦截器
   initHandlerExceptionResolvers(context);
   //初始化视图预处理器
   initRequestToViewNameTranslator(context);
   //初始化视图转换器
   initViewResolvers(context);
   //FlashMap管理器
   initFlashMapManager(context);
}

至此就是MVC的初始化阶段。

MVC的调用阶段

来到DispatcherServletdoService()方法,它调用了doDispatch(request, response);方法,这是调用的开始。

/** 中央控制器,控制请求的转发 **/
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 {
         // 1.检查是否是文件上传的请求
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         // 2.取得处理当前请求的controller,这里也称为hanlder处理器,
         mappedHandler = getHandler(processedRequest);
         // 如果handler为空,则返回404
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
         //3. 获取处理request的处理器适配器handler adapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         // 处理 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;
            }
         }

         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.
         // 4.实际的处理器处理请求,返回结果视图对象
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         // 结果视图对象的处理
         applyDefaultViewName(processedRequest, mv);
         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);
      }
      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);
         }
      }
   }
}

先看getHandler(HttpServletRequest request)方法:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = obtainApplicationContext().getBean(handlerName);
   }

   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   if (CorsUtils.isCorsRequest(request)) {
      CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
   return executionChain;
}

返回了一个HandlerExecutionChain,这个对象封装了handler和interceptors。

再来看HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());,此时获取到了处理request的处理器适配器handler adapter。

之后mv = ha.handle(processedRequest, response, mappedHandler.getHandler());,进入了AbstractHandlerMethodAdapter类,返回的是ModelAndView

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return handleInternal(request, response, (HandlerMethod) handler);
}

进入handleInternal(request, response, (HandlerMethod) handler);

@Override
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 = 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...
      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,这个方法里有一个getModelAndView(mavContainer, modelFactory, webRequest)方法,能获取到ModelAndView

回到doDispatch()方法,继续往下
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
processDispatchResult()方法调用了render(mv, request, response);

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 = 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.isDebugEnabled()) {
      logger.debug(""Rendering view ["" + view + ""] in DispatcherServlet with name '"" + getServletName() + ""'"");
   }
   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 + ""] in DispatcherServlet with name '"" +
               getServletName() + ""'"", ex);
      }
      throw ex;
   }
}

进入render方法可以看到,它先解析成为view:view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
然后调用view.render(mv.getModelInternal(), request, response);

@Override
public 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<String, Object> mergedModel = createMergedOutputModel(model, request, response);
   prepareResponse(request, response);
   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

在这个render()中,调用了renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);,这是实际的展现模型数据到视图的调用。

@Override
protected final void renderMergedOutputModel(
      Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
      throws Exception {

   T wireFeed = newFeed();
   buildFeedMetadata(model, wireFeed, request);
   buildFeedEntries(model, wireFeed, request, response);

   setResponseContentType(request, response);
   if (!StringUtils.hasText(wireFeed.getEncoding())) {
      wireFeed.setEncoding(""UTF-8"");
   }

   WireFeedOutput feedOutput = new WireFeedOutput();
   ServletOutputStream out = response.getOutputStream();
   feedOutput.output(wireFeed, new OutputStreamWriter(out, wireFeed.getEncoding()));
   out.flush();
}

即通过response以io流的方式,输出到浏览器。

至此MVC的调用结束。

文章已创建 17

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部