Spring Mvc原理分析(一)

草堂三師兄發表於2021-08-31

Servlet生命週期瞭解

Servlet的生命(週期)是由容器(eg:Tomcat)管理的,換句話說,Servlet程式設計師不能用程式碼控制其生命。
載入和例項化:時機取決於web.xml的定義,如果有x則在容器啟動時(eg:SSM),反之則在第一次針對這個Servlet的請求發生時(eg:Spring Boot)。
初始化(init):例項化後會立馬進行初始化,也就是執行init方法,init方式只會執行一次。
請求處理:初始化後,Servlet就可以接受請求了,基本方式是執行Servlet介面中的service方法。
終止服務:容器會在合適的時候銷燬某個Servlet物件,這個策略取決於容器的開發者/商,銷燬時destroy方法會被呼叫。

核心處理請求流程圖

在這裡插入圖片描述

入口

在這裡插入圖片描述
前端控制器DispatcherServlet也是一個Servlet,他父類的父類HttpServletBean覆寫了Servlet介面的init方法,在容器第一次載入或者第一次請求時會觸發(延遲載入),這個方法是Sring Mvc初始化的入口。

啟動初始化

在這裡插入圖片描述

容器啟動

  1. Spring容器啟動過程,會執行Bean的載入、建立和初始化,此處以Controller層為例分析,暫不關注其他型別資源。
  2. RequestMappingHandlerMapping類也是其中一個Bean,負責解析所有標識有@Controller或者@RequestMapping註解的Bean。
  3. RequestMappingHandlerMapping的父類實現了InitializingBean介面,覆寫了afterPropertiesSet()方法,該介面是Spring的擴充套件點之一,在Bean初始化過程中,所i有屬性注入完畢之後,會執行一系列回撥(回撥入口:AbstractAutowireCapableBeanFactory#initializeBean),其中一個回撥會驗證當前類是否實現了InitializingBean介面,如果實現了會呼叫afterPropertiesSet()方法,此方法是解析Controller層路徑和方法對應關係的入口。
  4. 解析完畢之後會儲存在AbstractHandlerMethodMapping#MappingRegistry中,控制器方法HandlerMethod儲存了當前路徑對應方法的主要資訊,它只負責準備資料,封裝資料,而而不提供具體使用的方式方法。
  5. 在接收請求時會先根據路徑從urlLookup 中獲取匹配條件,然後根據匹配條件獲取控制器方法HandlerMethod。
    在這裡插入圖片描述
class MappingRegistry {
    // T:RequestMappingInfo    =>   <請求匹配條件,控制器方法>
    private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    // T:RequestMappingInfo   =>    <路徑,請求匹配條件>
    private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
}
public class HandlerMethod {
   /** Logger that is available to subclasses */
   protected final Log logger = LogFactory.getLog(getClass());
   // Web控制器方法所在的Web控制器bean。可以是字串,代表bean的名稱;也可以是bean例項物件本身。
    private final Object bean;
    // Bean工程,如果bean屬性是Sring的beanName就可以用beanName獲取到對應的bean作用Handler
   @Nullable
    private final BeanFactory beanFactory;
   // Web控制器方法所在的Web控制器bean的型別,如果該bean被代理,這裡記錄的是被代理的使用者類資訊
    private final Class<?> beanType;
   // Web控制器方法
   private final Method method;
   private final Method bridgedMethod;
   // Web控制器方法的引數資訊:所在類所在方法,引數,索引,引數型別
   private final MethodParameter[] parameters;
   // 註解@ResponseStatus的code屬性
   @Nullable
   private HttpStatus responseStatus;
   // 註解@ResponseStatus的reason屬性
   @Nullable
   private String responseStatusReason;
   @Nullable
   private HandlerMethod resolvedFromHandlerMethod;
      ....
   }

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

策略初始化

protected void initStrategies(ApplicationContext context) {
    // 初始化檔案解析器,用於支援伺服器的檔案上傳
   initMultipartResolver(context);
   // 初始化國際化解析器,用來提供國際化功能;
   initLocaleResolver(context);
   // 初始化主題解析器,用來提供皮膚主題功能;
   initThemeResolver(context);
   // 初始化處理器對映器
   initHandlerMappings(context);
   // 初始化處理器介面卡,為不同的處理器提供上下文執行環境;
   initHandlerAdapters(context);
   // 處理器異常解析器,用來解析處理器產生的異常;
   initHandlerExceptionResolvers(context);
   // 初始化檢視邏輯名稱轉換器,根據邏輯檢視的名稱找到具體的檢視。
   // 當處理器沒有返回邏輯檢視名時,將請求的URL自動對映為邏輯檢視名;
   initRequestToViewNameTranslator(context);
   // 初始化檢視解析器,當控制器返回後,通過試圖解析器會把邏輯檢視名進行解析,從而定位實際檢視;
   initViewResolvers(context);
   // 初始化FlashMap管理器介面,負責重定向時,儲存引數到臨時儲存中
   initFlashMapManager(context);
}

對映器初始化

private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;
   // 預設獲取應用上下文所有的處理器對映
   if (this.detectAllHandlerMappings) {
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
       //   獲取所有的處理器對映
       Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
          // 賦值,用於請求時查詢
         this.handlerMappings = new ArrayList<>(matchingBeans.values());
         // We keep HandlerMappings in sorted order.
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   else {
      try {
          // 根據名稱獲取指定的bean:handlerMapping
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm);
      }
      catch (NoSuchBeanDefinitionException ex) {
         // Ignore, we'll add a default HandlerMapping later.
      }
   }
   // Ensure we have at least one HandlerMapping, by registering
   // a default HandlerMapping if no other mappings are found.
   if (this.handlerMappings == null) {
       // 獲取預設的處理器對映
       // 從【DispatcherServlet.properties】檔案中讀取預設配置
       // BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isDebugEnabled()) {
         logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
      }
   }
}

介面卡初始化(和對映器邏輯一致)

private void initHandlerAdapters(ApplicationContext context) {
   this.handlerAdapters = null;

   if (this.detectAllHandlerAdapters) {
      // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
      Map<String, HandlerAdapter> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerAdapters = new ArrayList<>(matchingBeans.values());
         // We keep HandlerAdapters in sorted order.
         AnnotationAwareOrderComparator.sort(this.handlerAdapters);
      }
   }
   else {
      try {
         HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
         this.handlerAdapters = Collections.singletonList(ha);
      }
      catch (NoSuchBeanDefinitionException ex) {
         // Ignore, we'll add a default HandlerAdapter later.
      }
   }

   // Ensure we have at least some HandlerAdapters, by registering
   // default HandlerAdapters if no other adapters are found.
   if (this.handlerAdapters == null) {
      this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
      if (logger.isDebugEnabled()) {
         logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
      }
   }
}

請求處理

流程圖

在這裡插入圖片描述

doDispatch方法解析

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Object dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                // 2.獲取處理器對映器
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 獲取不到handler 拋異常或者返回404
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
                // 4.獲取處理器介面卡器
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                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;
                    }
                }
                // 6.迴圈呼叫攔截器的preHandle方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                // 8.實際執行程式碼,handler通過反射執行控制器方法
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //  預設檢視
                this.applyDefaultViewName(processedRequest, mv);
                // 10.迴圈呼叫攔截的postHandle
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }
            // 11.處理結果,進行檢視解析 &  模板引擎渲染 & request域填充
            // 12.內部會呼叫攔截器的afterCompletion方法
            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            // 目標方法完成之後執行
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
             // 目標方法完成之後執行
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

獲取處理器對映器

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        Iterator var2 = this.handlerMappings.iterator();

        while(var2.hasNext()) {
            HandlerMapping mapping = (HandlerMapping)var2.next();
            // 核心方法獲取執行鏈
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }

    return null;
}
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 獲取handler
    Object handler = this.getHandlerInternal(request);
    if (handler == null) {
        handler = this.getDefaultHandler();
    }
    if (handler == null) {
        // 不滿足,返回
        return null;
    } else {
        if (handler instanceof String) {
            String handlerName = (String)handler;
            handler = this.obtainApplicationContext().getBean(handlerName);
        }
        // 以handler為引數獲取執行鏈:建立一個執行鏈物件,handler賦值到內部變數,新增所有攔截器
        HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
        ...
        ...
        return executionChain;
    }
}

獲取處理器介面卡

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        Iterator var2 = this.handlerAdapters.iterator();

        while(var2.hasNext()) {
            HandlerAdapter adapter = (HandlerAdapter)var2.next();
            // 核心判斷方法,找到支援該handler的介面卡
            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");
}

執行攔截器前置處理preHandle方法

HandlerExecutionChain#applyPreHandle

  • 攔截器的preHandle方法任意一個返回false則訪問不到目標方法
  • 攔截器的afterCompletion方法一定會執行
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = this.getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
            HandlerInterceptor interceptor = interceptors[i];
            // 迴圈呼叫
            if (!interceptor.preHandle(request, response, this.handler)) {
                // 執行完畢攔截器的所有AfterCompletio方法後return
                this.triggerAfterCompletion(request, response, (Exception)null);
                // 一個返回false即停止迴圈
                return false;
            }
        }
    }
    return true;
}

執行控制器的目標方法

在這裡插入圖片描述
InvocableHandlerMethod#doInvoke

@Nullable
protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(this.getBridgedMethod());

    try {
        // method.invoke(obj,args);
        // 反射呼叫目標類的目標方法
        // 目標方法:this.getBridgedMethod()
        // this.getBean()獲取handler中的bean,即為容器中的目標類例項/可能是一個CGLIB增強後的代理物件
        return this.getBridgedMethod().invoke(this.getBean(), args);
    } catch (IllegalArgumentException var4) {
        this.assertTargetBean(this.getBridgedMethod(), this.getBean(), args);
        String text = var4.getMessage() != null ? var4.getMessage() : "Illegal argument";
        throw new IllegalStateException(this.formatInvokeError(text, args), var4);
    } catch (InvocationTargetException var5) {
        Throwable targetException = 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);
        }
    }
}

處理返回結果 & 執行攔截器afterCompletion方法

DispatcherServlet#processDispatchResult

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) {
            this.logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException)exception).getModelAndView();
        } else {
            Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
            mv = this.processHandlerException(request, response, handler, exception);
            errorView = mv != null;
        }
    }

    if (mv != null && !mv.wasCleared()) {
        // a.檢視解析 & 模板引擎渲染
        this.render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    } else if (this.logger.isTraceEnabled()) {
        this.logger.trace("No view rendering, null ModelAndView returned.");
    }

    if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        if (mappedHandler != null) {
            // b.呼叫攔截器afterCompletion方法
            mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
        }

    }
}

a.檢視解析 & 模板引擎渲染

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);
    String viewName = mv.getViewName();
    View view;
    if (viewName != null) {
        // 檢視解析
        view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
        }
    } 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 '" + this.getServletName() + "'");
        }
    }

    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Rendering view [" + view + "] ");
    }

    try {
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        // 模板引擎渲染
        view.render(mv.getModelInternal(), request, response);
    } catch (Exception var8) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Error rendering view [" + view + "]", var8);
        }

        throw var8;
    }
}

b.呼叫攔截器afterCompletion方法,一定會執行

HandlerExecutionChain#afterCompletion

public void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
    HandlerInterceptor[] interceptors = this.getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for(int i = this.interceptorIndex; i >= 0; --i) {
            HandlerInterceptor interceptor = interceptors[i];

            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            } catch (Throwable var8) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
            }
        }
    }

}

同一路徑時啟動報錯原始碼位置

AbstractHandlerMethodMapping#assertUniqueMethodMapping
在這裡插入圖片描述

private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) {
    // 根據路徑資訊獲取方法資訊
    // 一個路徑對應2個方法時第二個方法解析時會獲取到上個方法的資訊
   HandlerMethod handlerMethod = this.mappingLookup.get(mapping);
   // 根據路徑獲取到方法資訊  並且  2個不是同一個方法時報錯提示已經存在
   if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) {
      throw new IllegalStateException(
            "Ambiguous mapping. Cannot map '" +    newHandlerMethod.getBean() + "' method \n" +
            newHandlerMethod + "\nto " + mapping + ": There is already '" +
            handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
   }
}

相關文章