【spring】spring mvc請求原始碼流程
阿新 • • 發佈:2022-03-13
spring mvc請求簡單介紹
- 本文原始碼基於spring-framework-5.3.10。
- mvc是spring原始碼中的一個子模組!
原始碼執行流程
- 使用者傳送請求至前端控制器DispatcherServlet。
- DispatcherServlet收到請求呼叫處理器對映器HandlerMapping。處理器對映器根據請求url找到具體的處理器,生成處理器執行鏈HandlerExecutionChain(包括處理器物件和處理器攔截器)一併返回給DispatcherServlet。
- DispatcherServlet根據處理器Handler獲取處理器介面卡HandlerAdapter,執行HandlerAdapter處理一系列的操作,如:引數封裝,資料格式轉換,資料驗證等操作
- 執行處理器Handler(Controller,也叫頁面控制器)。Handler執行完成返回ModelAndView、HandlerAdapter將Handler執行結果ModelAndView返回到DispatcherServlet。
- DispatcherServlet將ModelAndView傳給ViewReslover檢視解析器。ViewReslover解析後返回具體View
- DispatcherServlet對View進行渲染檢視(即將模型資料model填充至檢視中)。
- DispatcherServlet響應使用者。
原始碼執行流程圖
spring mvc中的一核心元件
-
DispatcherServlet
- HandlerMapping: 負責根據請求的URL和配置@RequestMapping對映去匹配, 匹配到會返回Handler(具體控制器的方法)
- HandlerAdaper: 負責呼叫Handler-具體的方法- 返回檢視的名字 Handler將它封裝到ModelAndView(封裝檢視名,request域的資料)
- ViewReslover: 根據ModelAndView裡面的檢視名地址去找到具體的jsp封裝在View物件中
- View:進行檢視渲染(將jsp轉換成html內容 --這是Servlet容器的事情了) 最終response到的客戶端
原始碼分析
/**
* 最核心的控制器
* 原始碼位置:org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse)
*/
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 {
// 驗證是不是上傳的請求,上傳的請求會轉化為MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 進行對映
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 找到最合適的HandlerAdapter,按照順序,那個先解析到就是那個
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler. HTTP快取相關
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置攔截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不進行後續處理了
return;
}
// Actually invoke the handler.
// 獲取引數,執行方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果mv有 檢視沒有,給你設定預設檢視
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);
}
}
}
}
獲取元件相關邏輯:誰先解析到就用誰!
/**
* 獲取處理器對映器
* 原始碼位置:org.springframework.web.servlet.DispatcherServlet.getHandler(HttpServletRequest)
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/** 拿到所有handlerMappings (容器啟動階段初始化:拿到所有實現了HandlerMapping的Bean)
* @see DispatcherServlet#initHandlerMappings
* 測試發現: 不同的HandlerMapping可以有相同path, 誰先解析到就用哪個
* */
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
/**
* 獲取處理器介面卡
* 原始碼位置:org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(Object)
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 按照配置的順序,誰先解析到就用那個
for (HandlerAdapter adapter : this.handlerAdapters) {
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");
}
獲取引數,執行方法原始碼分析
/**
* 獲取引數,執行方法最外層的呼叫
* 原始碼位置:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(HttpServletRequest, HttpServletResponse, Object)
*/
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 直接呼叫這個方法
return handleInternal(request, response, (HandlerMethod) handler);
}
/**
* 獲取引數,執行方法內部的呼叫邏輯
* 原始碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod)
*/
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 檢查當前請求的method是否為支援的method(預設Null,可通過繼承AbstractController設定supportedMethods)
// 檢查當前請求是否必須session (預設false,可通過繼承AbstractController設定requireSession)
checkRequest(request);
/**
* 判斷當前是否需要支援在同一個session中只能線性地處理請求:一個session同時只能處理一個執行緒
* 因為鎖是通過 synchronized 是 JVM 程序級,所以在分散式環境下,
* 無法達到同步相同 Session 的功能。預設情況下,synchronizeOnSession 為 false
*/
if (this.synchronizeOnSession) {
// 獲取當前請求的session物件
HttpSession session = request.getSession(false);
if (session != null) {
// 為當前session生成一個唯一的可以用於鎖定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 對HandlerMethod進行引數等的適配處理,並呼叫目標handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果當前不存在session,則直接對HandlerMethod進行適配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// *如果當前不需要對session進行同步處理,則直接對HandlerMethod進行適配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//判斷當前請求頭中是否包含Cache-Control請求頭,如果不包含,則對當前response進行處理
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 如果當前SessionAttribute中存在配置的attributes,則為其設定過期時間。
// 這裡SessionAttribute主要是通過@SessionAttribute註解生成的
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
// 如果當前不存在SessionAttributes,則判斷當前是否存在Cache-Control設定,
// 如果存在,則按照該設定進行response處理,如果不存在,則設定response中的
// Cache的過期時間為-1,即立即失效
prepareResponse(response);
}
}
return mav;
}
/**
* 獲取引數,執行方法前的準備邏輯
* 原始碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)
*/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 把我們的請求req resp包裝成 ServletWebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 獲取容器中全域性配置的InitBinder和當前HandlerMethod所對應的Controller中
// 配置的InitBinder,用於進行引數的繫結
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 獲取容器中全域性配置的ModelAttribute和當前HandlerMethod所對應的Controller 中配置的ModelAttribute,
// 這些配置的方法將會在目標方法呼叫之前進行呼叫
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 封裝handlerMethod,會在呼叫前解析引數、呼叫後對返回值進行處理
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 讓invocableMethod擁有引數解析能力
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 讓invocableMethod擁有返回值處理能力
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 讓invocableMethod擁有InitBinder解析能力
invocableMethod.setDataBinderFactory(binderFactory);
// 設定ParameterNameDiscoverer,該物件將按照一定的規則獲取當前引數的名稱
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndView處理容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 將request的Attribute複製一份到ModelMap
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// *呼叫我們標註了@ModelAttribute的方法,主要是為我們的目標方法預載入
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 重定向的時候,忽略model中的資料 預設false
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 獲取當前的AsyncWebRequest,這裡AsyncWebRequest的主要作用是用於判斷目標
// handler的返回值是否為WebAsyncTask或DeferredResult,如果是這兩種中的一種,
// 則說明當前請求的處理應該是非同步的。所謂的非同步,指的是當前請求會將Controller中
// 封裝的業務邏輯放到一個執行緒池中進行呼叫,待該呼叫有返回結果之後再返回到response中。
// 這種處理的優點在於用於請求分發的執行緒能夠解放出來,從而處理更多的請求,提高吞吐。
// 只有待目標任務完成之後才會回來將該非同步任務的結果返回。
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// 封裝非同步任務的執行緒池、request、interceptors到WebAsyncManager中
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();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// *對請求引數進行處理,呼叫目標HandlerMethod,並且將返回值封裝為一個ModelAndView物件
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 對封裝的ModelAndView進行處理,主要是判斷當前請求是否進行了重定向,如果進行了重定向,
// 還會判斷是否需要將FlashAttributes封裝到新的請求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
/**
* 獲取引數,執行方法
* 原始碼位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object...)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*真正的呼叫我們的目標物件 很重要 很重要*/
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 設定相關的返回狀態
setResponseStatus(webRequest);
// 如果請求處理完成,則設定requestHandled屬性
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
// 如果請求失敗,但是有錯誤原因,那麼也會設定requestHandled屬性
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 遍歷當前容器中所有ReturnValueHandler,判斷哪種handler支援當前返回值的處理,
// 如果支援,則使用該handler處理該返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
渲染檢視邏輯
/**
* 渲染檢視邏輯
* 原始碼位置:org.springframework.web.servlet.DispatcherServlet.processDispatchResult(HttpServletRequest, HttpServletResponse, HandlerExecutionChain, ModelAndView, Exception)
*/
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);
}
}
// Did the handler return a view to render?
if (mv != 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 != null) {
// Exception (if any) is already handled.. 攔截器:AfterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
結束語
- 獲取更多本文的前置知識文章,以及新的有價值的文章,讓我們一起成為架構師!
- 關注公眾號,可以讓你對MySQL、併發程式設計、spring原始碼有深入的瞭解!
- 關注公眾號,後續持續高效的學習JVM!
- 這個公眾號,無廣告!!!每日更新!!!