本文內(nèi)容假設(shè)閱讀者對(duì)Spring MVC 源碼閱讀-框架啟動(dòng)過程中包含的知識(shí)點(diǎn)已經(jīng)有所了解。
本文的配置也是基于xml形式進(jìn)行的岩遗。
文章中的一些稱呼的說明
1.在Controller中創(chuàng)建的@RequestMapping
在下文中稱為路徑映射
2.在Controller中創(chuàng)建的路徑映射方法在下文中稱為請(qǐng)求處理方法
@GetMapping("/a") // 路徑映射
public String a() { // 請(qǐng)求處理方法
return "";
}
關(guān)于框架啟動(dòng)過程的文章中捧请,已經(jīng)指出Spring MVC是基于
DispatcherServlet
展開的,而DispatcherServlet
又繼承自GenericServlet
锅必,
也就是說Servlet
的生命周期代表了Spring MVC
的生命周期。
其中init(ServletConfig)
代表了Spring MVC的啟動(dòng),那么service(ServletRequest, ServletResponse)
也就應(yīng)該代表Spring MVC的請(qǐng)求的處理流程秉犹。
1. Spring MVC 請(qǐng)求處理流程
從DispatcherServlet
向上尋找service(ServletRequest, ServletResponse)
,最終會(huì)在HttpServlet
中找到該方法稚晚。
// HttpServlet
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
// 這里的service方法也需要從DispatcherServlet開始自下向上查找崇堵,
// 可以發(fā)現(xiàn)最早出現(xiàn)該方法的是FrameworkServlet,那這個(gè)方法調(diào)用的就是FrameworkServlet里面的
service(request, response);
}
簡單的對(duì)
ServletRequest
和ServletResponse
進(jìn)行了類型轉(zhuǎn)換客燕,隨后調(diào)用service(HttpServletRequest, HttpServletResponse)
方法
1.1. FrameworkServlet#service(HttpServletRequest, HttpServletResponse)
當(dāng)請(qǐng)求剛到來時(shí)鸳劳,第一步就是確認(rèn)請(qǐng)求方式,以便使用特定的方式對(duì)請(qǐng)求進(jìn)行業(yè)務(wù)處理也搓。
HttpServlet#service(HttpServletRequest, HttpServletResponse)
中提供了針對(duì)GET
赏廓、HEADER
、POST
傍妒、PUT
幔摸、DELETE
、OPTIONS
颤练、TRACE
7種請(qǐng)求方式的處理抚太。
FrameworkServlet#service(HttpServletRequest, HttpServletResponse)
中提供了PATCH
請(qǐng)求方式的處理。
由于方法太多昔案,在下文中將圍繞POST
請(qǐng)求方式展開邏輯描述尿贫。
// FrameworkServlet
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
// HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
// ...
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
// 這里的doPost方法也需要從DispatcherServlet開始自下向上查找,F(xiàn)rameworkServlet類中最早出現(xiàn)該方法
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
// FrameworkServlet
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 不管是doPost踏揣、doGet還是doDelete等庆亡,它們都要調(diào)用了這個(gè)方法
processRequest(request, response);
}
1.2 FrameworkServlet#processRequest(HttpServletRequest, HttpServletResponse)
processRequest
方法中做了這么幾件事情
1.將HttpServletRequest
和HttpServletResponse
對(duì)象 以及 從HttpServletRequest
中解析出來的Locale
存放到線程本地內(nèi)。
2.根據(jù)當(dāng)前的請(qǐng)求創(chuàng)建一個(gè)異步管理程序來支持對(duì)異步返回值的處理捞稿。
3.調(diào)用請(qǐng)求處理的方法(doService(request, response);
)又谋。
4.進(jìn)行收尾工作
~ 清除本次請(qǐng)求產(chǎn)生的數(shù)據(jù)
~ 調(diào)用注冊(cè)的銷毀邏輯
~ 對(duì)Session
的內(nèi)容進(jìn)行一些操作
~ 向容器內(nèi)發(fā)送一個(gè)ServletRequestHandledEvent
類型的事件
// FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// ---------------------------------------------------- 獲取 Locale ---------------------------------------------------
// 從 ThreadLocal 中獲取與當(dāng)前線程相關(guān)聯(lián)的 LocaleContext,LocaleContext 里面存放的是一個(gè) Locale 對(duì)象娱局。
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// buildLocaleContext方法彰亥,也要從 DispatcherServlet 類向上找,最早在 DispatcherServlet 中找到該方法衰齐。
// 默認(rèn)情況下任斋,會(huì)使用到在Spring MVC框架啟動(dòng)過程中初始化的 LocaleResolver 來解析請(qǐng)求中的 Locale 對(duì)象
LocaleContext localeContext = buildLocaleContext(request);
// ---------------------------------------------- 獲取 RequestAttributes -----------------------------------------------
// 從 ThreadLocal 中獲取與當(dāng)前線程相關(guān)聯(lián)的 RequestAttributes,RequestAttributes 內(nèi)存儲(chǔ)的是 request耻涛、response等內(nèi)容
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 通過創(chuàng)建 ServletRequestAttributes對(duì)象實(shí)例 來 存儲(chǔ)Request和Response废酷。
// 除此之外瘟檩,ServletRequestAttributes 還提供了 對(duì)Request和Session屬性的查找與修改,使用見之后的分析澈蟆。
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// ------------------------------------------ Spring MVC 對(duì)異步請(qǐng)求的支持 ------------------------------------------
// 創(chuàng)建 WebAsyncManager 對(duì)象墨辛,并放到 request 中
// 如果當(dāng)前請(qǐng)求是因?yàn)?AsyncContext#dispatch() 方法的調(diào)用而產(chǎn)生,那么這個(gè)請(qǐng)求內(nèi)部已經(jīng)被Servlet容器設(shè)置了這個(gè)值趴俘。
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 使用指定的key注冊(cè)一個(gè)攔截器睹簇,在 異步返回值(例如:Callable) 被執(zhí)行前后會(huì)被調(diào)用。
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// ------------------------------------------- 對(duì) ThreadLocal 進(jìn)行設(shè)置 -------------------------------------------
// 將 localeContext 和 requestAttributes 存放到 ThreadLocal 中
// (其中又具體區(qū)分 ThreadLocal 是否是可繼承的寥闪,上面的帶有previous的LocaleContext與RequestAttributes的獲取都有涉及带膀,這里不做說明)
initContextHolders(request, localeContext, requestAttributes);
// --------------------------------------------- 處理請(qǐng)求 ---------------------------------------------------------
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 {
// --------------------------------------------- 請(qǐng)求完成之后的收尾工作 -------------------------------------------
// 重設(shè)線程本地的數(shù)據(jù)(相當(dāng)于清除本次請(qǐng)求的信息)
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
/* 注意,這是 ServletRequestAttributes 內(nèi)的收尾工作
* 1. 調(diào)用注冊(cè)的跟request有關(guān)的(也就是scope為0)銷毀方法 (雖然注冊(cè)時(shí)使用的是Runnable類型橙垢,但執(zhí)行時(shí)是直接調(diào)用run方法的垛叨,不是多線程執(zhí)行的)
* 2. 對(duì)Session屬性進(jìn)行一些更新
* 3. 標(biāo)記requestActive為false
*/
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
// 向 webApplicationContext 容器內(nèi)發(fā)送一個(gè) ServletRequestHandledEvent 類型的事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
// DispatcherServlet
protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
// 這里獲取的就是Spring MVC啟動(dòng)過程中,initLocaleResolver 策略中加載的默認(rèn)本地化解析器(AcceptHeaderLocaleResolver)
LocaleResolver lr = this.localeResolver;
if (lr instanceof LocaleContextResolver) {
// 如果是CookieLocaleResolver柜某、SessionLocaleResolver嗽元、FixedLocaleResolver才會(huì)到這里
return ((LocaleContextResolver) lr).resolveLocaleContext(request);
}
else {
// 默認(rèn)的 AcceptHeaderLocaleResolver 會(huì)進(jìn)入這里。
// 這里的返回值是一個(gè)lambda函數(shù)喂击,其實(shí)是一個(gè)匿名內(nèi)部類的寫法剂癌。
return () -> (lr != null ? lr.resolveLocale(request) : request.getLocale());
}
}
// FrameworkServlet
protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
@Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {
if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
return new ServletRequestAttributes(request, response);
}
else {
return null; // preserve the pre-bound RequestAttributes instance
}
}
// FrameworkServlet
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
// WebAsyncUtils
public static WebAsyncManager getAsyncManager(ServletRequest servletRequest) {
WebAsyncManager asyncManager = null;
Object asyncManagerAttr = servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE);
if (asyncManagerAttr instanceof WebAsyncManager) {
asyncManager = (WebAsyncManager) asyncManagerAttr;
}
if (asyncManager == null) {
asyncManager = new WebAsyncManager();
servletRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager);
}
return asyncManager;
}
1.2.1 ServletRequestAttributes 的使用示例
public void test() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
// 獲取 request 和 response
HttpServletRequest request = requestAttributes.getRequest();
HttpServletResponse response = requestAttributes.getResponse();
HttpServletRequest request1 = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
// 如果scope為0,那么操作的就是此次請(qǐng)求的屬性翰绊,也就是request的請(qǐng)求佩谷;如果scope為1,那么操作的就是session的屬性监嗜。
Object requestAttr = requestAttributes.getAttribute("屬性名", 0);
Object sessionAttr = requestAttributes.getAttribute("屬性名", 1);
requestAttributes.removeAttribute("屬性名", 0);
requestAttributes.removeAttribute("屬性名", 1);
// 注冊(cè)一些自定義的邏輯谐檀,在請(qǐng)求處理完后會(huì)在掃尾工作時(shí)調(diào)用這些邏輯,雖然這里是Runnable類型裁奇,但并不是多線程執(zhí)行的桐猬,而是直接調(diào)用的run方法。
requestAttributes.registerDestructionCallback("1", () -> {}, 0);
// 獲取session的一些內(nèi)容
String sessionId = requestAttributes.getSessionId();
Object sessionMutex = requestAttributes.getSessionMutex();
HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
}
1.3 DispatcherServlet#doService(HttpServletRequest, HttpServletResponse)
doService
方法中中大概做了這么幾件事情
1.對(duì)由include
發(fā)起的請(qǐng)求做特殊處理刽肠。
2.將框架內(nèi)的屬性交給HttpServletRequest
溃肪,以方便之后的處理流程使用。
~ Spring MVC 的 IOC容器
~ Local 的解析器
~ Theme 的解析器
~ FlashMap 相關(guān)內(nèi)容
3.調(diào)用請(qǐng)求處理的方法(doDispatch(request, response);
)音五。
// DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// --------------------------------------- 對(duì)RequestDispatcher#include造成的請(qǐng)求的處理 --------------------------------------------
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
//
/*
* 檢查請(qǐng)求中是否有 javax.servlet.include.request_uri 屬性惫撰,如果有,就保留請(qǐng)求屬性的快照躺涝,以便能夠在include之后恢復(fù)原始屬性厨钻。
*
* 這個(gè)東西和 RequestDispatcher#include(ServletRequest, ServletResponse) 有關(guān),具體內(nèi)容不做說明。
* 發(fā)揮作用的地方例如: <jsp:incluede page="xxx.jsp"/>
*/
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// ------------------------------------------ 將框架內(nèi)的一些對(duì)象塞到request中 --------------------------------------------
// 將 Spring MVC框架內(nèi)的一些對(duì)象 放到request內(nèi)部莉撇,以使之后的 各種處理器 和 View 使用呢蛤。
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); // Spring MVC的IOC容器
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// ----------------------------------------- FlashMapManager的處理 -------------------------------------------------------
// 這里就是 FlashMapManager 策略起作用的地方惶傻,它的使用在之前 Spring MVC 框架的啟動(dòng)流程中講過
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
// 將FlashMap放入InputFlashMap中
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
// 因?yàn)檫@是請(qǐng)求的入口棍郎,所以O(shè)utputFlashMap內(nèi)不應(yīng)該存在任何值
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
// ------------------------------------------ 處理請(qǐng)求 -------------------------------------------------------
try {
doDispatch(request, response);
}
finally {
// 判斷當(dāng)前請(qǐng)求已經(jīng)調(diào)用過 AsyncWebRequest#startAsync(),不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里银室。
// 如果當(dāng)前請(qǐng)求沒有變更為異步模式的話涂佃,這個(gè)條件才會(huì)成立,如果請(qǐng)求處理方法返回的是異步返回值蜈敢,那條件就不成立辜荠。
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// 在 include 調(diào)度完成后,恢復(fù)上面快照內(nèi)存儲(chǔ)的請(qǐng)求原始屬性
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
1.4 DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)
// DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// HandlerExecutionChain 實(shí)際上是 HandlerMethod 和 相應(yīng)的攔截器 的組合體
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// -------------------------------------------- 對(duì) multipart/ 類型請(qǐng)求的識(shí)別(例如文件上傳) ------------------------------------------
// 檢查請(qǐng)求是否是跟multipart/類型的請(qǐng)求相關(guān)抓狭,如果符合條件就把request轉(zhuǎn)為MultipartHttpServletRequest實(shí)現(xiàn)伯病。
// 條件是: 1.有相應(yīng)Multipart Request解析器 并且 2.是Multipart Request
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// -------------------------------------------- 尋找匹配的請(qǐng)求處理方法 ---------------------------------------------
// 從 解析路徑映射的處理策略 中 查找到要執(zhí)行的請(qǐng)求處理方法。 由于寫的示例項(xiàng)目是基于注解掃描的否过,所以這里會(huì)使用 RequestMappingHandlerMapping 實(shí)現(xiàn)來查找午笛。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response); // 默認(rèn)情況下,沒找到匹配的路徑映射就響應(yīng)客戶端404
return;
}
// ---------------------------------------- 尋找合適的 HandlerMethod 執(zhí)行適配器 -------------------------------------
// 判斷Hanlder/Controller是如何實(shí)現(xiàn)的苗桂,來使用特定的適配器執(zhí)行我們的`請(qǐng)求處理方法`药磺,在示例項(xiàng)目種默認(rèn)情況下會(huì)獲取到 RequestMappingHandlerAdapter 。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ------------------------------------------ GET/HDEA請(qǐng)求的緩存機(jī)制 ------------------------------------------
// https://blog.csdn.net/snail_cjc/article/details/50778855
// Process last-modified header, if supported by the handler.
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;
}
}
// ------------------------------------------- 執(zhí)行攔截器的PreHandle方法 --------------------------------------------
// 運(yùn)行攔截器的preHandle方法煤伟,如果有攔截器的preHandle返回了false癌佩,那么此次請(qǐng)求直接停止,同時(shí)調(diào)用攔截器的afterCompletion方法便锨。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// ---------------------------------------------- 交由HandlerAdapter處理請(qǐng)求 --------------------------------------------------------
// 調(diào)用`請(qǐng)求處理方法`執(zhí)行邏輯的地方
mv = ha.handler(processedRequest, response, mappedHandler.getHandler());
// 判斷當(dāng)前請(qǐng)求已經(jīng)調(diào)用過 AsyncWebRequest#startAsync()围辙,不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里。
// 如果調(diào)用過放案,代表當(dāng)前請(qǐng)求變更為異步模式酌畜,也就是說如果請(qǐng)求處理方法返回的是異步返回值,那條件就成立卿叽。
if (asyncManager.isConcurrentHandlingStarted()) {
return; // 如果請(qǐng)求已經(jīng)轉(zhuǎn)為異步模式桥胞,這里直接返回null,不需要執(zhí)行下面的代碼考婴。
}
// ------------------------------------------- RequestToViewNameTranslator 邏輯的應(yīng)用 ------------------------------------
// 如果`請(qǐng)求處理方法`執(zhí)行完成后贩虾,仍然沒有ViewName被設(shè)置(例如:請(qǐng)求處理方法 在 @Controller注解的類中 并且 方法返回值類型為void),
// 那么就使用 Spring MVC 框架啟動(dòng)過程中初始化的 RequestToViewNameTranslator 策略將請(qǐng)求地址解析為ViewName,
// 如果解析到的ViewName不為空沥阱,就填充到傳入的 mv 中缎罢。
applyDefaultViewName(processedRequest, mv);
// ------------------------------------------- 執(zhí)行攔截器的PostHandle方法 --------------------------------------------
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 {
// 判斷當(dāng)前請(qǐng)求是否已經(jīng)轉(zhuǎn)為異步模式,如果是,則條件成立策精,不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里舰始。
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
// 調(diào)用 AsyncHandlerInterceptor 類型攔截器的 applyAfterConcurrentHandlingStarted 方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 清理 MultipartRequest 所使用的資源(文件上傳方向的內(nèi)容)。
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch
都做了什么咽袜?
1.檢查請(qǐng)求是否跟文件上傳相關(guān)丸卷,以及確認(rèn)相關(guān)后的一些轉(zhuǎn)換。(與MultipartResolver
策略相關(guān))
2.通過請(qǐng)求找到要執(zhí)行的請(qǐng)求處理方法
询刹。(與HandlerMapping
策略相關(guān))
3.通過判斷請(qǐng)求處理方法
所處的類是使用哪種方式成為Handler
來找到合適的HandlerAdapter
谜嫉。(與HandlerAdapter
策略相關(guān))
4.調(diào)用攔截器的preHandle
方法,如果被攔下來了凹联,就執(zhí)行攔截器的afterCompletion
方法沐兰。
5.通過獲取到的HandlerAdapter
來正式執(zhí)行請(qǐng)求處理方法
。
6.判斷是否需要將請(qǐng)求路徑解析成ViewName蔽挠。(與RequestToViewNameTranslator
策略相關(guān))
7.調(diào)用攔截器的postHandle
方法住闯。
8.判斷是否需要渲染視圖煌珊。(與ViewResolvers
策略相關(guān))
9.最后調(diào)用攔截器的afterCompletion
方法兼蜈。(這一步和第8步是在一個(gè)方法內(nèi)編寫的,但兩件事沒關(guān)系)
10.清理multipart request
所產(chǎn)生的資源占用踱葛。
假設(shè)我們使用`@RestController`注解來使類成為`Handler`偶惠,
那么實(shí)際上在`第5步`春寿,`請(qǐng)求處理方法`的返回值就已經(jīng)寫入了Response發(fā)送給了客戶端,
在這種情況下忽孽,第6绑改、8步驟實(shí)際上就不會(huì)發(fā)生,因?yàn)檫@兩部與模板引擎技術(shù)相關(guān)兄一,而在假設(shè)中厘线,我們沒有使用這一技術(shù);
如果我們`@Controller`注解來使類稱為`Handler`出革,同時(shí)沒有使用`@ResponseBody`造壮,那么第6、8步驟將會(huì)作用于`請(qǐng)求處理方法`的返回值骂束。
同時(shí)耳璧,在這種假設(shè)下,我們的返回值可以為:沒有返回值展箱、null旨枯、ViewName、View混驰、ModelAndeView
補(bǔ)充:
1.ViewName 實(shí)際上指的就是一個(gè)字符串而已攀隔,不過這個(gè)字符串會(huì)被認(rèn)為指向了一個(gè)頁面模板或者一個(gè)重定向地址等皂贩,最終會(huì)由不同的 ViewResolvers 解析為具體的 View 實(shí)現(xiàn)。
2.View 是 ViewName 的終點(diǎn)昆汹,每種模板引擎技術(shù)明刷,都會(huì)為我們提供一個(gè)可用的 View 實(shí)現(xiàn),這里可以簡單理解為它就是個(gè)頁面满粗。
3.如果 ModelAndeView 調(diào)用無參構(gòu)造函數(shù)創(chuàng)建辈末,那實(shí)際上和返回一個(gè)null區(qū)別不大。
上面的一串步驟看起來還是很復(fù)雜败潦,這里再簡化一下本冲,方便記憶准脂。
1.對(duì)Http請(qǐng)求
進(jìn)行處理劫扒。
2.找到請(qǐng)求處理方法
。
3.找到合適的HandlerAdapter
狸膏。
4.執(zhí)行攔截器的先處理方法沟饥。
5.交由HandlerAdapter
執(zhí)行請(qǐng)求處理方法
。
6.調(diào)用攔截器的后處理方法湾戳。
7.渲染頁面模板
8.清理與收尾贤旷。
在下文會(huì)具體深入說明這些步驟的實(shí)現(xiàn)邏輯
1.4.1 尋找匹配的請(qǐng)求處理方法
// DispatcherServlet
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 默認(rèn)情況下 handlerMappings 里存儲(chǔ)的是 Spring MVC 框架啟動(dòng)時(shí)初始化的兩個(gè)用于解析路徑映射的處理策略
// BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
// AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
/* 這里要結(jié)合類結(jié)構(gòu)圖來看
* 如果當(dāng)前 HandlerMapping 是 BeanNameUrlHandlerMapping,則會(huì)前往 AbstractUrlHandlerMapping#getHandlerInternal
* 如果當(dāng)前 HandlerMapping 是 RequestMappingHandlerMapping砾脑,則會(huì)前往 AbstractHandlerMethodMapping#getHandlerInternal
*
* 最終返回的是要執(zhí)行的請(qǐng)求處理方法幼驶。
*/
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
// 如果處理后handler為字符串, 根據(jù)該字符串拿bean
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 為給定的 請(qǐng)求處理方法 構(gòu)建HandlerExecutionChain,它內(nèi)部包括適用的攔截器韧衣。
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
// 配置Cors (跨域資源共享)
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
// AbstractHandlerMethodMapping
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 獲取到要尋找的路徑映射
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
// 從 AbstractHandlerMethodMapping.MappingRegistry的實(shí)例對(duì)象內(nèi) 找到對(duì)應(yīng)的 請(qǐng)求處理方法盅藻。
// AbstractHandlerMethodMapping.MappingRegistry 對(duì)象的構(gòu)建在之前的文章內(nèi)有提到,這里就不重復(fù)了
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
// AbstractHandlerMapping
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 轉(zhuǎn)換 handler 的類型
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 獲取到要尋找的路徑映射
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
// 找到和請(qǐng)求路徑匹配的攔截器畅铭,關(guān)于攔截器的添加方式在下文進(jìn)行說明
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
HandlerExecutionChain
實(shí)際上就是HandlerMethod
和Spring MVC攔截器
的組合體氏淑。
1.4.1.1 Spring MVC 攔截器的配置
Spring MVC 默認(rèn)情況下有2種添加方式。
// 第一種添加方式
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
// 第二種添加方式(手動(dòng)配置HandlerMapping)
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<list>
<bean class="MyInterceptor"/>
</list>
</property>
</bean>
// 配置起作用的原理如下
// AbstractHandlerMapping
protected void initApplicationContext() throws BeansException {
// 一個(gè)空的實(shí)現(xiàn)
extendInterceptors(this.interceptors);
// 對(duì)應(yīng)第一種配置方式
detectMappedInterceptors(this.adaptedInterceptors);
// 對(duì)應(yīng)第二種配置方式
initInterceptors();
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
1.4.2 尋找合適的HandlerMethod執(zhí)行適配器
因?yàn)槭褂玫氖?code>@Controller/@RestController創(chuàng)建的
Handler
硕噩,所以最后得到的是RequestMappingHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
// 默認(rèn)情況下 handlerAdapters 里存儲(chǔ)的是 Spring MVC 框架啟動(dòng)時(shí)初始化的三個(gè)功能擴(kuò)展適配器假残,每個(gè)都對(duì)應(yīng)了一類Handler的注冊(cè)方式。
// 在上一篇文章中展示了有4種創(chuàng)建Handler的方式炉擅,這里不再重復(fù)辉懒。
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
// 因?yàn)槲覀兪褂玫氖?@Controller/@RestController 創(chuàng)建的Handler,所以返回的是 RequestMappingHandlerAdapter
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");
}
1.4.3 交由HandlerAdapter處理請(qǐng)求
在之前我們就確認(rèn)了谍失,在示例代碼的默認(rèn)情況下最終會(huì)由
RequestMappingHandlerAdapter
來執(zhí)行眶俩,所以直接看它的代碼。
// AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
// RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 對(duì) 請(qǐng)求方式 和 Session 的一些檢查
checkRequest(request);
// ------------------------ 會(huì)話級(jí)上進(jìn)行代碼同步執(zhí)行 ---------------------------------
// 在會(huì)話級(jí)別上進(jìn)行代碼同步執(zhí)行袱贮,使得來自同一會(huì)話的請(qǐng)求串行訪問該控制器仿便,沒遇到過合適的使用場景体啰。
// 默認(rèn)情況下,這個(gè)值是false.
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 {
// ------------------------- 默認(rèn)情況下嗽仪,執(zhí)行請(qǐng)求處理方法 ------------------------
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
// RequestMappingHandlerAdapter
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
/*
* 創(chuàng)建一個(gè)持有request和response的ServletWebRequest對(duì)象荒勇。
*
* 這個(gè)類的特殊之處在于它實(shí)現(xiàn)了NativeWebRequest接口,能夠調(diào)用很多HttpServletRequest沒有實(shí)現(xiàn)的方法闻坚,
* 在操作Last-Modified時(shí)或許會(huì)用到這個(gè)對(duì)象沽翔。
*/
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// ------------------------------ @InitBinder注解 的方法的相關(guān)的處理 -----------------------------
// 獲取到所有有關(guān)的 @InitBinder 注解的方法,并把他們包裝為一個(gè) WebDataBinderFactory 工廠對(duì)象(新建)
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// --------------------------- @SessionAttributes和@ModelAttribute注解 的方法的相關(guān)的處理 -----------------------------
/*
* 獲取到 sessionAttributeStore 參數(shù)窿凤,并包裝為一個(gè) SessionAttributesHandler 對(duì)象(新建) 作為 param3仅偎,
* sessionAttributeStore 的值可以在 Spring MVC 的xml文件中進(jìn)行配置,默認(rèn)實(shí)現(xiàn)是DefaultSessionAttributeStore雳殊。
*
* 獲取到所有有關(guān)的 @ModelAttribute 注解的方法 作為 param1橘沥。
*
* 將構(gòu)建的param1、param3和傳入的binderFactor(param2)夯秃,這3個(gè)參數(shù)包裝為一個(gè) ModelFactory 工廠對(duì)象(新建)
*/
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// ------------------------------- 對(duì)ServletInvocableHandlerMethod的進(jìn)一步組裝 ----------------------------------
// 使用 ServletInvocableHandlerMethod 對(duì)象實(shí)例包裝 handlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 設(shè)置參數(shù)解析器座咆,argumentResolvers 的賦值過程在 Spring MVC 框架啟動(dòng)時(shí)進(jìn)行
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 設(shè)置返回值解析器,returnValueHandlers 的賦值過程在 Spring MVC 框架啟動(dòng)時(shí)進(jìn)行
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 設(shè)置跟 @InitBinder 注解有關(guān)的工廠對(duì)象
invocableMethod.setDataBinderFactory(binderFactory);
// 作用就跟名字一樣---參數(shù)名詞發(fā)現(xiàn)者仓洼,用于確認(rèn)方法介陶、構(gòu)造函數(shù)的參數(shù)名稱等作用,這個(gè)參數(shù)可以在Spring MVC的xml文件中配置色建,默認(rèn)實(shí)現(xiàn)是 DefaultParameterNameDiscoverer
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ------------------------------ 對(duì)ModelAndViewContainer對(duì)象的進(jìn)一步組裝 ---------------------------------------------
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 取出前文存放到request中的flashMap哺呜,然后存放到mavContainer里持有的的Model中
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 把一些相關(guān)的數(shù)據(jù)全部放到 Model 里面 (和 @SessionAttributes 和 @ModelAttribute 兩個(gè)注解相關(guān)的內(nèi)容的處理)
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 設(shè)置是否在重定向時(shí)忽略 Model, 不然Model內(nèi)的數(shù)據(jù)會(huì)被拼接到url上, true 就是忽略。
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// ------------------------------ 對(duì)異步請(qǐng)求相關(guān)的處理 -----------------------------
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// 如果當(dāng)前請(qǐng)求是因?yàn)?AsyncContext#dispatch() 方法的調(diào)用而產(chǎn)生箕戳,那么這個(gè)請(qǐng)求內(nèi)部已經(jīng)被Servlet容器設(shè)置了這個(gè)值某残,這里獲取的也就是之前請(qǐng)求創(chuàng)建的那個(gè)實(shí)例。
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
// Callable的攔截器
asyncManager.registerCallableInterceptors(this.callableInterceptors);
// DeferredResult的攔截器
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// 如果上一個(gè)請(qǐng)求內(nèi)已經(jīng)設(shè)置了并發(fā)處理結(jié)果漂羊,那么就不需要再執(zhí)行原來的請(qǐng)求處理方法了驾锰,直接處理這里得到的并發(fā)處理結(jié)果就行。
if (asyncManager.hasConcurrentResult()) { // 能進(jìn)入這個(gè)判斷的請(qǐng)求走越,必定是由 AsyncContext#dispatch() 方法的調(diào)用而產(chǎn)生請(qǐng)求椭豫。
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 + "]";
});
// 這里把并發(fā)處理結(jié)果再包裝成一下,為了配合下面那行代碼的邏輯旨指。
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// ------------------------------ 執(zhí)行請(qǐng)求處理方法并處理返回值 ------------------------------------
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 判斷當(dāng)前請(qǐng)求是否已經(jīng)轉(zhuǎn)為異步模式赏酥,如果是,則條件成立谆构,不理解可以看完下面對(duì)異步請(qǐng)求的說明后再回頭看這里裸扶。
if (asyncManager.isConcurrentHandlingStarted()) { // 這個(gè)判斷是作用域非 dispatch 導(dǎo)致的請(qǐng)求
return null;
}
// ------------------------------ 收尾工作 ----------------------------------
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
/* 做了如下三件事情:
* 調(diào)用我們自己注冊(cè)的所有的 DestructionCallback 方法。 (在上面ServletRequestAttributes的使用示例中有注冊(cè)示例)
* 將我們通過特定方式訪問的Session屬性進(jìn)行回寫搬素。
* 把 ServletWebRequest 中的 requestActive 標(biāo)記為 false呵晨。
*/
webRequest.requestCompleted();
}
}
1.4.3.1 @InitBinder注解的方法的處理
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
// RequestMappingHandlerAdapter
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
// 通過 HandlerMethod 獲取到 Hanlder 類型
Class<?> handlerType = handlerMethod.getBeanType();
/*
* 從下面兩段代碼可以看出來 @InitBinder 有多種用法魏保,起碼這里就有2種了
* 1.在 Hanlder 類中直接定義
* 2.跟 @ControllerAdvice 注解配合使用
*/
// -------------------------------- 在Handler中跟 @InitBinder 有關(guān)的代碼 -----------------------------------
// 從當(dāng)前HandlerMethod所處的類中找到定義的所有@InitBinder注解的方法
// 首先從緩存中獲取
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
// 把通過反射找到的類中所有 @InitBinder 方法都放到緩存中,來避免每次都通過反射獲取
this.initBinderCache.put(handlerType, methods);
}
// ------------------------ 跟 @ControllerAdvice 和 @InitBinder 有關(guān)的代碼 ------------------------
// 從所有被 @ControllerAdvice注解的類中摸屠,找到作用范圍包含當(dāng)前 HandlerMethod所處的類 的第一個(gè)谓罗,
// 然后將其中定義的所有@InitBinder注解的方法取出來
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
// initBinderAdviceCache 的賦值發(fā)生在Spring MVC 啟動(dòng)過程中(對(duì) HandlerAdapter 策略初始化的時(shí)候)
// key與@ControllerAdvice注解的類有關(guān) value是其中定義的被@InitBinder注解的所有方法
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
// 和@ControllerAdvice注解的作用范圍有關(guān),這個(gè)注解也是可以配置屬性的季二,例如:basePackages檩咱、assignableTypes等
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method)); // 太過細(xì)節(jié),不做過多關(guān)注
}
}
});
// -------------------- 將兩種方式找到的 @InitBinder 合并到一個(gè)集合中 --------------------------------
// 將 methods 集合轉(zhuǎn)換類型并存放到 initBinderMethods 中胯舷,initBinderMethods 是個(gè)List集合
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
// 使用 ServletRequestDataBinderFactory 對(duì)象包裝 這些 @InitBinder方法刻蚯,以方便后續(xù)使用
return createDataBinderFactory(initBinderMethods);
}
// RequestMappingHandlerAdapter
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
// RequestMappingHandlerAdapter
public WebBindingInitializer getWebBindingInitializer() {
return this.webBindingInitializer;
}
從上面的代碼中可以直觀的看出來
@InitBinder
的2種使用方式,實(shí)際上有3種桑嘶,
第三種就是getWebBindingInitializer()
獲取的webBindingInitializer
炊汹,這個(gè)東西我們可以通過配置來設(shè)置。
下面進(jìn)行展示3種使用方法不翩。
// com.demo.i.controller.MyController
// 第一種用法(在Handler中直接使用兵扬,這種方式只針對(duì)當(dāng)前Handler生效)
@RestController
public class MyController {
@InitBinder
public void initBinder(WebDataBinder binder) {
// do something...
}
}
// 第二種用法(配合@ControllerAdvice進(jìn)行全局配置麻裳,這種方式只針對(duì)掃描到的Handler生效)
// 如果@ControllerAdvice不指定范圍口蝠,則代表針對(duì)所有Handler生效
@ControllerAdvice // (basePackages = "com.demo.i.controller")
public class MyControllerAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
// do something...
}
}
// 第三種用法(全局配置)
@Bean
public RequestMappingHandlerAdapter webBindingInitializer() {
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setWebBindingInitializer(new WebBindingInitializer() {
// WebBindingInitializer中有兩個(gè)入?yún)⒌姆椒ㄒ呀?jīng)被啟用
@Override
public void initBinder(WebDataBinder binder) {
// do something...
}
});
return adapter;
}
1.4.3.2 @SessionAttributes和@ModelAttribute注解的方法的處理
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
// 注意,這里是沒有@RequestMapping注解津坑,但有@ModelAttribute注解妙蔗,不要少看了那個(gè)!
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
// RequestMappingHandlerAdapter
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// -------------------------------- 找到關(guān)聯(lián)的SessionAttributesHandler ----------------------------
// -------------------------------- 這個(gè)東西是處理 @SessionAttributes 注解用的 -----------------------
// 找到和當(dāng)前 handlerMethod 有關(guān)系的 SessionAttributesHandler
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
// 通過 HandlerMethod 獲取到 Hanlder 類型
Class<?> handlerType = handlerMethod.getBeanType();
// -------------------------------- 找到Handler中被 @ModelAttribute 注釋的方法 -------------------------------
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
// ------------------------ 跟 @ControllerAdvice 和 @ModelAttribute 有關(guān)的代碼 ------------------------
// 從所有被 @ControllerAdvice注解的類中疆瑰,找到作用范圍包含當(dāng)前 HandlerMethod所處的類 的第一個(gè)眉反,
// 然后將其中定義的所有被@ModelAttribute注解的方法取出來
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first
// modelAttributeAdviceCache 的賦值發(fā)生在Spring MVC 啟動(dòng)過程中(對(duì) HandlerAdapter 策略初始化的時(shí)候)
// key與@ControllerAdvice注解的類有關(guān) value是其中定義的被@InitBinder注解的所有方法
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
// -------------------- 將兩種方式找到的方法合并到一個(gè)集合中 --------------------------------
// 將 methods 集合轉(zhuǎn)換類型并存放到 attrMethods 中,attrMethods 是個(gè)List集合
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
// RequestMappingHandlerAdapter
private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
// 通過 HandlerMethod 獲取到 Hanlder 類型
Class<?> handlerType = handlerMethod.getBeanType();
// sessionAttributesHandlerCache 使用的Map是 ConcurrentHashMap 實(shí)現(xiàn)
SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
// DCL模式(Double Check Lock)
if (sessionAttrHandler == null) {
synchronized (this.sessionAttributesHandlerCache) {
sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
if (sessionAttrHandler == null) {
// 創(chuàng)建一個(gè)跟這個(gè)Handler有關(guān)系的SessionAttributesHandler對(duì)象實(shí)例穆役。
// sessionAttributeStore 默認(rèn)使用的是 DefaultSessionAttributeStore 實(shí)現(xiàn)寸五。
sessionAttrHandler = new SessionAttributesHandler(handlerType, this.sessionAttributeStore);
this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);
}
}
}
return sessionAttrHandler;
}
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
this.sessionAttributeStore = sessionAttributeStore;
// 這里是對(duì) @SessionAttributes 注解的處理,嘗試從Handler上面找到 @SessionAttributes注解
SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
if (ann != null) {
Collections.addAll(this.attributeNames, ann.names());
Collections.addAll(this.attributeTypes, ann.types());
}
this.knownAttributeNames.addAll(this.attributeNames);
}
1.4.3.2.1 @ModelAttribute
從上面的代碼中可以直觀的看出來
@ModelAttribute
的2種總體使用方式耿币,下面進(jìn)行展示2種總體使用方法梳杏。
關(guān)于
Model
、ModelMap
淹接、ModelAndView
十性,這是內(nèi)容都是要配合ViewResolver
使用的。
也就是說塑悼,這些內(nèi)容都是和模板引擎有關(guān)的劲适,可以理解為就是負(fù)責(zé)在控制器和展現(xiàn)數(shù)據(jù)的視圖之間傳遞數(shù)據(jù)用的。
其實(shí)這里的Model
體現(xiàn)的就是MVC模式中的M
厢蒜,在下文中出現(xiàn)的Model
霞势、ModelMap
實(shí)際指的是一個(gè)東西烹植。
// 第一種用法(在Handler中直接使用,這種方式只針對(duì)當(dāng)前Handler生效)
@Controller
public class MyController {
// 提供了更加詳細(xì)的用法
// 如果訪問 /test愕贡,這個(gè)方法會(huì)比test更早執(zhí)行
// 搶先接收到參數(shù)a刊橘,然后進(jìn)行處理,最后以a作為key添加到Model中颂鸿,在test中嘗試從Model中獲取屬性a時(shí)就可以拿到了
@ModelAttribute
public void myModelAttribute(@RequestParam String a, Model model) {
Map<String, Object> map = model.asMap();
map.put("a", a + " -> myModelAttribute -> test");
}
// 如果訪問 /test促绵,這個(gè)方法會(huì)比test更早執(zhí)行
// 搶先接收到參數(shù)a,然后進(jìn)行處理嘴纺,最后以b作為key返回給Model败晴,在test中嘗試從Model中獲取屬性b時(shí)就可以拿到了
@ModelAttribute("b") // @ModelAttribute的value值將作為key,方法的返回值將作為value栽渴,這個(gè)value可以不寫尖坤,但Spring內(nèi)部解析時(shí)會(huì)變得復(fù)雜,使程序運(yùn)行變慢
public String myModelAttribute1(@RequestParam String a) {
return a + " -> myModelAttribute1 -> test";
}
// 測試請(qǐng)求 Get localhost/test?a=1
@ResponseBody
@GetMapping("/test")
public String test(@ModelAttribute("a") String a, @ModelAttribute("b") String b) {
return a + "\n" + b;
}
@GetMapping("/page/test")
public String test(Model model) { // model 在頁面模板上就可以使用
return "test"; // ViewName
}
}
// 第二種使用方式(配合@ControllerAdvice進(jìn)行全局配置闲擦,這種方式只針對(duì)掃描到的Handler生效)
// 如果@ControllerAdvice不指定范圍慢味,則代表針對(duì)所有Handler生效
@ControllerAdvice // (basePackages = "com.demo.i.controller")
public class MyControllerAdvice {
@ModelAttribute
public void myModelAttribute() {
// do something...
}
}
1.4.3.2.2 @SessionAttribute 和 @SessionAttributes
根據(jù)源碼,可以看出來
@SessionAttributes
是要加在Handler上來使用的墅冷。
@SessionAttributes
用于在Session中存儲(chǔ)Model內(nèi)部的數(shù)據(jù)纯路,@SessionAttribute
則用于獲取存儲(chǔ)在Session中的數(shù)據(jù)。
@Controller
@SessionAttributes(names = {"a", "b"})
public class MyController {
@RequestMapping("/test1")
public String test1(ModelMap modelMap, HttpSession session) {
modelMap.addAttribute("a", "1");
modelMap.addAttribute("b", "2");
session.setAttribute("c", "10");
return "redirect:/test2";
}
@RequestMapping("/test2")
public String test2(ModelMap modelMap, HttpSession session) {
// ModelMap 中可以獲取到 @SessionAttributes 注解中標(biāo)記的屬性寞忿,但獲取不到直接存入Session中的屬性
System.out.println(modelMap.get("a")); // 1
System.out.println(modelMap.get("b")); // 2
System.out.println(modelMap.get("c")); // null
// 可以直接通過Session獲取到@SessionAttributes存儲(chǔ)的屬性
System.out.println(session.getAttribute("a")); // 1
System.out.println(session.getAttribute("b")); // 2
System.out.println(session.getAttribute("c")); // 10
return "redirect:/test3";
}
@RequestMapping("/test3")
public String test3(@SessionAttribute("a") String a,
@SessionAttribute("c") String c,
SessionStatus sessionStatus) {
// @SessionAttribute 注解可以獲取到Session內(nèi)的數(shù)據(jù)
System.out.println(a); // 1
System.out.println(c); // 10
// 清除 @SessionAttributes 注解標(biāo)記的屬性驰唬,對(duì)應(yīng)的數(shù)據(jù)會(huì)從Session中刪除
sessionStatus.setComplete();
return "redirect:/test4";
}
@RequestMapping("/test4")
public String test4(HttpSession session) {
// 因?yàn)樯弦粋€(gè)請(qǐng)求中已經(jīng)清除了 @SessionAttributes 對(duì)屬性的標(biāo)記,所以a和b已經(jīng)被刪除了
System.out.println(session.getAttribute("a")); // null
System.out.println(session.getAttribute("b")); // null
System.out.println(session.getAttribute("c")); // 10
return "test4"; // ViewName
}
}
補(bǔ)充腔彰,有一個(gè)
@RequestAttribute
注解叫编,它干的活和@SessionAttribute
一樣,只不過它的作用范圍是RequestScope
霹抛。
1.4.3.3 對(duì)ModelAndViewContainer對(duì)象的進(jìn)一步組裝
1.4.3.3.1 將FlashMap中的屬性存入Model中
// 取出前文存放到request中的flashMap搓逾,然后存放到mavContainer里持有的的Model中
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
在上面的
RequestMappingHandlerAdapter#invokeHandlerMethod
源碼里有如上這么一行代碼,包括本文的上面也有對(duì)FlashMapManager
的處理杯拐,再結(jié)合我們之前在Spring的啟動(dòng)過程一文中說過的FlashMap
的用法霞篡,這里進(jìn)行一下補(bǔ)充。
@PostMapping("/a")
public void a(HttpServletRequest request, HttpServletResponse response) throws IOException {
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
flashMap.put("a", "1");
flashMap.put("b", "2");
String redirect = "/b";
// 將 FlashMap 交由 FlashMapManager 管理
RequestContextUtils.saveOutputFlashMap(redirect, request, response);
response.sendRedirect(redirect);
}
@GetMapping("/b")
public String b(HttpServletRequest request, ModelMap modelMap) {
Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
Object returnValue = null;
if (inputFlashMap != null) {
returnValue = inputFlashMap.get("a");
}
return (String) returnValue;
}
相對(duì)于之前提供的寫法藕施,這里只是在b方法上添加了一個(gè)
ModelMap
入?yún)ⅰ?br> 這里要補(bǔ)充的就是寇损,根據(jù)上面的源碼的意思,可以在目標(biāo)方法上使用Model
接收到FlashMap
內(nèi)的數(shù)據(jù)裳食。
1.4.3.3.2 將和@SessionAttributes和@ModelAttribute相關(guān)的屬性存入Model中
// ModelFactory
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
// 從 Session 中獲取 @SessionAttributes 中指定的屬性集
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
// 合并屬性矛市,將 sessionAttributes 內(nèi)的數(shù)據(jù)也添加到 Model 中
container.mergeAttributes(sessionAttributes);
// 運(yùn)行 @ModelAttribute 注解注釋的方法,如果有返回值 且 binding = true诲祸,則將返回值添加到 Model 中浊吏。
invokeModelAttributeMethods(request, container);
// 找到所有被 @ModelAttribute 注解 同時(shí) 又被 @SessionAttributes 指定的參數(shù)
for (String name : findSessionAttributeArguments(handlerMethod)) {
// 如果 Model 中不包含這個(gè)屬性而昨,就嘗試從 Session 中獲取這個(gè)屬性然后添加到 Model 中,如果 Session 里面也沒有這個(gè)參數(shù)就直接報(bào)錯(cuò)找田。
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
// ModelFactory
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
// modelMethods 內(nèi)容的填充是在 RequestMappingHandlerAdapter#getModelFactory 方法的最后一行創(chuàng)建 ModelFactory 對(duì)象實(shí)例時(shí)進(jìn)行的歌憨。
// 它實(shí)際上存放的就是被 @ModelAttribute 注解注釋的方法。
while (!this.modelMethods.isEmpty()) {
// 獲取 @ModelAttribute 注解的方法
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
// 獲取到 @ModelAttribute 注解
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
// 判斷 @ModelAttribute 中設(shè)置的 name/value 值是否在 Model 中存在
if (container.containsAttribute(ann.name())) {
// 這個(gè)是 binding = false 時(shí)的情況墩衙,默認(rèn)情況下 binding = true务嫡,這里不具體說了,意思很明確了
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}
// 這下面是處理 binding = true 的情況
// 調(diào)用這個(gè)被 @ModelAttribute 注解注釋的方法
Object returnValue = modelMethod.invokeForRequest(request, container);
if (!modelMethod.isVoid()){
// 獲取到 @ModelAttribute 注解設(shè)置的value值漆改;如果沒有設(shè)置心铃,則會(huì)通過反射將返回值的類型解析為value
// 所以還是盡量寫上value值
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
// 如果 Model 中沒有這個(gè)屬性,則將該屬性添加到 Model 中
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
上面的代碼中提供了一種
@SessionAttributes
和@ModelAttribute
結(jié)合起來的使用方法挫剑,示例代碼如下去扣。
// 都是一些花里胡哨的用法。
@RestController
@SessionAttributes("test")
public class Test {
@ModelAttribute
public void a(HttpSession session) {
session.setAttribute("test", "123");
}
@RequestMapping("/test1")
public String test1(@ModelAttribute("test") String test) {
return test;
}
}
1.4.3.4 執(zhí)行請(qǐng)求處理方法并處理返回值
// ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// ----------------------------------------- 執(zhí)行請(qǐng)求處理方法 -----------------------------------------
// 這里面主要就是使用參數(shù)解析器對(duì)方法入?yún)⒌慕馕龇疲馕鐾陞?shù)后直接使用反射調(diào)用請(qǐng)求處理方法愉棱,這里就不細(xì)說了,感興趣自己看
// 其中也會(huì)使用到在在之前就已經(jīng)準(zhǔn)備好的 @InitBinder 注解的相關(guān)的方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// ----------------------------------------- 設(shè)置響應(yīng)狀態(tài) -------------------------------------------
// 通過 @ResponseStatus 方式主動(dòng)設(shè)置
setResponseStatus(webRequest);
if (returnValue == null) { // 判斷請(qǐng)求值是否為null
/* 這里有3個(gè)條件哲戚,只要有一個(gè)達(dá)成就行
* 第一個(gè)和 Get 類型請(qǐng)求的緩存有關(guān)奔滑,如果符合緩存情況整個(gè)判斷就為true。
* 第二個(gè)和 @ResponseStatus 注解有關(guān)惫恼,如果有這個(gè)注解整個(gè)判斷就為true档押。
* 第三個(gè) 用來在ModelAndView中表示請(qǐng)求已經(jīng)執(zhí)行完成
*/
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) { // 判斷 @ResponseStatus 有沒有設(shè)置 reason 值,如果設(shè)置了祈纯,就不需要處理返回值了。
mavContainer.setRequestHandled(true); // 標(biāo)記請(qǐng)求已經(jīng)處理完成叼耙。
return;
}
// ----------------------------------------- 處理 請(qǐng)求處理方法的返回值 ------------------------------------
// 如果請(qǐng)求處理方法有返回值且沒有使用@ResponseStatus注解腕窥,就需要走下面的邏輯
mavContainer.setRequestHandled(false); // 標(biāo)記請(qǐng)求尚為完成處理
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 從 returnValueHandlers 中找到適合處理當(dāng)前返回值的 返回值處理器,將返回值交由返回值處理器處理
// 不同的返回值處理器中 會(huì)在各自合適的時(shí)機(jī)將 ModelAndViewContainer 中的 RequestHandled 設(shè)置為true
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
returnValueHandlers
也就是返回值處理器筛婉。
如果使用了方法直接或間接使用了@ResponseBody
注解簇爆,則使用RequestResponseBodyMethodProcessor
來處理返回值,通過I/O寫入response爽撒。
如果返回值使用ResponseEntity
入蛆,則使用HttpEntityMethodProcessor
來處理返回值。
里面有支持異步返回值的返回值處理器硕勿,例如AsyncTaskMethodReturnValueHandler
哨毁、CallableMethodReturnValueHandler
、DeferredResultMethodReturnValueHandler
源武。
補(bǔ)充: 如果方法沒有直接或間接使用
@ResponseBody
注解扼褪,那返回值則是有嚴(yán)格限制的想幻,可以試試返回int類型的值,Spring MVC會(huì)找不到合適的處理器而相應(yīng)請(qǐng)求處理失敗话浇。
1.4.3.5 收尾工作
收尾工作做了如下幾方面的工作脏毯。
1.對(duì)@SessionAttributes
注解的處理
2.組裝ModelAndView
對(duì)象
3.對(duì)FlashMap
的處理
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// -------------------------- 對(duì) @SessionAttributes 注解的處理 -----------------------------
// 將列為 @SessionAttributes 的 Model 屬性升級(jí)到會(huì)話級(jí)別,必要時(shí)添加 BindingResult 屬性幔崖。
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) { // 如果請(qǐng)求已經(jīng)處理完成
return null;
}
ModelMap model = mavContainer.getModel();
// ------------------------- 通過 ModelAndViewContainer 構(gòu)建 ModelAndView --------------------
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView()); // 設(shè)置視圖
}
// ------------------------- 處理 FlashMap 相關(guān)內(nèi)容 -----------------------------
// 判斷 Model 是否使 RedirectAttributes 類型食店,這個(gè)東西和 FlashMap 的使用有關(guān)。
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;
}
在上一篇說明Spring MVC框架啟動(dòng)過程的文章里赏寇,已經(jīng)說了一種我比較喜歡的
FlashMap
使用方法叛买。
在這里,從上面的源碼中可以看到另一種FlashMap
的使用方式蹋订,示例代碼如下展示率挣。
@GetMapping("/test1")
public String test1(RedirectAttributes attributes) {
attributes.addFlashAttribute("a", "10");
return "redirect:/test2";
}
@ResponseBody
@GetMapping("/test2")
public Object test2(ModelMap model) {
return model.getAttribute("a");
}
1.4.4 處理最終的視圖
// DispatcherServlet
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// 如果存在異常,就嘗試獲取異常相關(guān)的view
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()) {
// 如果請(qǐng)求處理程序返回了要渲染的視圖露戒,就渲染視圖
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) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
// DispatcherServlet
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// -------------------------------------- 處理 Locale ------------------------------
// 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) {
// 如果存在ViewName椒功,就通過合適的ViewResolver解析出來一個(gè)View。
// 通常我們使用模板引擎時(shí)會(huì)配置一個(gè)ViewResolver以及一個(gè)特定的View
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
// 如果沒有合適的ViewResolver智什,則會(huì)出現(xiàn)如下異常动漾,通常出現(xiàn)這個(gè)異常意味著我們模板引擎沒有配置好。
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// 如果我們?cè)谡?qǐng)求處理方法返回的時(shí)候直接創(chuàng)建好了View對(duì)象荠锭,則會(huì)進(jìn)入這里旱眯。
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.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 使用不同模板引擎時(shí),這個(gè)View對(duì)象的類型是不同的证九,往往是模板引擎的依賴內(nèi)自帶的删豺,通過它們內(nèi)部的細(xì)節(jié)進(jìn)行頁面渲染。
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
1.5 Spring MVC中的異步請(qǐng)求的邏輯
要想理解上面源碼中的異步請(qǐng)求的意義愧怜,首先要知道異步請(qǐng)求的邏輯是如何運(yùn)行的呀页,之后再回頭看上面的代碼,才能更好的理解拥坛。
這部分邏輯主要涉及4個(gè)類蓬蝶,WebAsyncManager
、AsyncWebRequest
猜惋、AsyncContext
丸氛、AsyncListener
。
AsyncWebRequest
使用的是StandardServletAsyncWebRequest
作為具體實(shí)現(xiàn)著摔。
AsyncContext
這個(gè)接口是Servlet
的技術(shù)缓窜。
它們的關(guān)系是這樣的:WebAsyncManager
類中持有一個(gè)StandardServletAsyncWebRequest
的對(duì)象實(shí)例,而StandardServletAsyncWebRequest
內(nèi)部又是對(duì)AsyncContext
接口的調(diào)用。
ServletRequest API文檔
AsyncContext API文檔
AsyncListener API文檔
1.5.1 異步返回值處理器
可以通過
返回值處理器
來作為理解這一塊邏輯的起點(diǎn)雹洗,Spring MVC中異步返回值常用的有Callable
香罐、DeferredResult
、StreamingResponseBody
等时肿。
這里以Callable
為例進(jìn)行說明庇茫,可以找到它的返回值處理器,也就是CallableMethodReturnValueHandler
螃成。
// 使用示例
// web.xml
<servlet>
<!-- Spring MVC DispatcherServlet 的配置 -->
<async-supported>true</async-supported>
</servlet>
@ResponseBody
@GetMapping("/test")
public Callable<String> test() {
return () -> {
Thread.sleep(1000 * 2);
return "test"; // 當(dāng)使用 Callable 時(shí)這里也可以返回 ViewName
};
}
// Callable類型返回值的處理器
public class CallableMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 對(duì) Callable 返回值的支持
return Callable.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
// returnValue 就是 請(qǐng)求處理方法 的返回值
Callable<?> callable = (Callable<?>) returnValue;
// 這里 getAsyncManager旦签,會(huì)從 request 中獲取之前存儲(chǔ)的 WebAsyncManager,隨和的startCallableProcessing才是主要邏輯
WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer);
}
}
1.5.2 WebAsyncManager#startCallableProcessing
要想全面的理解寸宏,這里要結(jié)合
RequestMappingHandlerAdapter#invokeHandlerMethod
方法的部分代碼片段來看宁炫。
// RequestMappingHandlerAdapter#invokeHandlerMethod 內(nèi)的代碼片段
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); // 注冊(cè) Callable 類型返回值的攔截器,可以在Xml文件中配置
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// WebAsyncManager
public void startCallableProcessing(Callable<?> callable, Object... processingContext) throws Exception {
Assert.notNull(callable, "Callable must not be null");
// 這里會(huì)把 Callable 包裝成 WebAsyncTask氮凝,請(qǐng)求處理方法 的返回值實(shí)際上可以直接是 WebAsyncTask 類型羔巢。
startCallableProcessing(new WebAsyncTask(callable), processingContext);
}
// WebAsyncManager
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext)
throws Exception {
Assert.notNull(webAsyncTask, "WebAsyncTask must not be null");
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
// 這里由于返回值類型是Callable,所以這里這個(gè)Timeout是空的罩阵,可以直接使用WebAsyncTask作為返回值竿秆,這樣就能具體設(shè)置超時(shí)時(shí)間
Long timeout = webAsyncTask.getTimeout();
if (timeout != null) {
this.asyncWebRequest.setTimeout(timeout);
}
// 這里由于返回值類型是Callable,所以這里這個(gè)Executor是空的稿壁,可以直接使用WebAsyncTask作為返回值幽钢,這樣就能具體設(shè)置Executor的實(shí)現(xiàn)
AsyncTaskExecutor executor = webAsyncTask.getExecutor();
if (executor != null) {
this.taskExecutor = executor;
}
else {
logExecutorWarning();
}
// ------------------------------------------------- 整合Callable處理攔截器 -------------------------------------------------
List<CallableProcessingInterceptor> interceptors = new ArrayList<>();
// 這個(gè)比較特殊,是在 WebAsyncTask#getInterceptor() 方法中定義的一個(gè)匿名內(nèi)部類
interceptors.add(webAsyncTask.getInterceptor());
// 在 RequestMappingHandlerAdapter#invokeHandlerMethod 中有批量注冊(cè)傅是,但默認(rèn)沒有值匪燕,用的時(shí)候需要自己配置
// 在 FrameworkServlet#processRequest 中注冊(cè)了一個(gè) RequestBindingInterceptor
interceptors.addAll(this.callableInterceptors.values());
// timeoutCallableInterceptor 是 TimeoutCallableProcessingInterceptor 類的實(shí)例對(duì)象
interceptors.add(timeoutCallableInterceptor);
final Callable<?> callable = webAsyncTask.getCallable();
// 將攔截器集合封裝成一個(gè)CallableInterceptorChain實(shí)例對(duì)象。
final CallableInterceptorChain interceptorChain = new CallableInterceptorChain(interceptors);
// ------------------------------------------------ 添加一系列的行為處理器 -------------------------------------------
// ------------------------ 這些行為處理器的邏輯會(huì)在AsyncListener的回調(diào)中被執(zhí)行喧笔,是一個(gè)重要的邏輯區(qū)域帽驯。 ----------------------
// 3(指在這個(gè)方法中發(fā)揮作用的順序)
// 執(zhí)行超時(shí)后的處理器 (#1.5.4 節(jié)可以看到調(diào)用)
this.asyncWebRequest.addTimeoutHandler(() -> {
logger.debug("Async request timeout for " + formatRequestUri());
Object result = interceptorChain.triggerAfterTimeout(this.asyncWebRequest, callable);
if (result != CallableProcessingInterceptor.RESULT_NONE) {
setConcurrentResultAndDispatch(result);
}
});
// 執(zhí)行異常后的處理器 (#1.5.4 節(jié)可以看到調(diào)用)
this.asyncWebRequest.addErrorHandler(ex -> {
logger.debug("Async request error for " + formatRequestUri() + ": " + ex);
Object result = interceptorChain.triggerAfterError(this.asyncWebRequest, callable, ex);
result = (result != CallableProcessingInterceptor.RESULT_NONE ? result : ex);
setConcurrentResultAndDispatch(result);
});
// 執(zhí)行完成后的處理器 (#1.5.4 節(jié)可以看到調(diào)用)
this.asyncWebRequest.addCompletionHandler(() ->
interceptorChain.triggerAfterCompletion(this.asyncWebRequest, callable));
// ----------------------------------- 調(diào)用攔截器的 beforeConcurrentHandling 方法 -----------------------------
interceptorChain.applyBeforeConcurrentHandling(this.asyncWebRequest, callable);
// ------------------------------------ 將Servlet容器委派的請(qǐng)求轉(zhuǎn)換為異步模式 ------------------------------------------
// 1(指在這個(gè)方法中發(fā)揮作用的順序)
startAsyncProcessing(processingContext);
// ----------------------------------- 對(duì) 請(qǐng)求處理方法的返回值 進(jìn)行再包裝 -------------------------------------------
// 將 Callable 類型的返回值封裝為 Future,然后提交給 taskExecutor 等待執(zhí)行溃斋。
try {
// 2(指在這個(gè)方法中發(fā)揮作用的順序)
Future<?> future = this.taskExecutor.submit(() -> {
Object result = null;
try {
// ---------------------------- 調(diào)用攔截器的 preProcess 方法(正序遍歷攔截器) -----------------------------
interceptorChain.applyPreProcess(this.asyncWebRequest, callable);
// ---------------------------- 執(zhí)行返回的 請(qǐng)求處理方法 的返回值 ----------------------------------------
// 這個(gè)方法如果執(zhí)行時(shí)間太久導(dǎo)致請(qǐng)求響應(yīng)超過了指定時(shí)間界拦,就會(huì)執(zhí)行超時(shí)的邏輯
result = callable.call();
}
catch (Throwable ex) { // 這里是可以響應(yīng)中斷邏輯的,當(dāng)超時(shí)后或Future執(zhí)行異常后梗劫,程序會(huì)對(duì)的線程進(jìn)行中斷
result = ex;
}
finally {
// ---------------------------- 調(diào)用攔截器的 postProcess 方法(倒序遍歷攔截器) -----------------------------
result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, result);
}
// 處理異步請(qǐng)求結(jié)果 并且 通過調(diào)用 AsyncContext#dispatch 來對(duì)當(dāng)前請(qǐng)求的URI再次調(diào)度 (意思就是會(huì)對(duì)當(dāng)前URI再發(fā)送一個(gè)請(qǐng)求)
// 能走到這里就代表 異步操作 是能完成的,代表會(huì)執(zhí)行完成的邏輯截碴。
setConcurrentResultAndDispatch(result);
});
// 設(shè)置的這個(gè) TaskFuture梳侨,在之后的超時(shí)和異常處理中有用到。
interceptorChain.setTaskFuture(future);
}
catch (RejectedExecutionException ex) {
Object result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, ex);
setConcurrentResultAndDispatch(result);
throw ex;
}
}
1.5.3 將請(qǐng)求轉(zhuǎn)換為異步模式
// WebAsyncManager
private void startAsyncProcessing(Object[] processingContext) {
synchronized (WebAsyncManager.this) {
// concurrentResult 是用來存放最后的處理結(jié)果的日丹,RESULT_NONE 代表沒有結(jié)果
this.concurrentResult = RESULT_NONE;
// 這里這個(gè)東西走哺,上面?zhèn)鬟^來的是ModelAndViewContainer
this.concurrentResultContext = processingContext;
}
// ------------------------------------ 將請(qǐng)求轉(zhuǎn)換為異步模式 ------------------------------------------
this.asyncWebRequest.startAsync();
if (logger.isDebugEnabled()) {
logger.debug("Started async request");
}
}
// StandardServletAsyncWebRequest
public void startAsync() {
Assert.state(getRequest().isAsyncSupported(),
"Async support must be enabled on a servlet and for all filters involved " +
"in async request processing. This is done in Java code using the Servlet API " +
"or by adding \"<async-supported>true</async-supported>\" to servlet and " +
"filter declarations in web.xml.");
Assert.state(!isAsyncComplete(), "Async processing has already completed");
// 如果當(dāng)前請(qǐng)求已經(jīng)調(diào)用過 startAsync,則直接返回哲虾,防止形成嵌套式調(diào)用丙躏。
if (isAsyncStarted()) {
return;
}
// 這里才是核心點(diǎn)择示,使用`startAsync`、`addListener`晒旅、`setTimeout`這些`Servlet`體系提供的方法栅盲,
// 來完成Spring MVC框架的異步請(qǐng)求的邏輯
this.asyncContext = getRequest().startAsync(getRequest(), getResponse()); // 將當(dāng)前請(qǐng)求轉(zhuǎn)換為異步模式
this.asyncContext.addListener(this); // 這里要求傳入的是一個(gè) AsyncListener 接口的實(shí)現(xiàn)類,接口內(nèi)定義了4個(gè)回調(diào)方法废恋。
if (this.timeout != null) {
this.asyncContext.setTimeout(this.timeout); // 整個(gè)異步請(qǐng)求的超時(shí)時(shí)間谈秫,由Servlet容器管理,超時(shí)后會(huì)回調(diào)超時(shí)的回調(diào)方法鱼鼓。
}
}
public boolean isAsyncStarted() {
// asyncContext 不為空代表請(qǐng)求已經(jīng)轉(zhuǎn)換為了異步模式拟烫。
// isAsyncStarted 則是檢查此請(qǐng)求是否已調(diào)用 startAsync 方法使其進(jìn)入異步模式。
return (this.asyncContext != null && getRequest().isAsyncStarted());
}
1.5.4 AsyncListener接口的實(shí)現(xiàn)類
// 這個(gè)參數(shù)要記住迄本,因?yàn)樵谏厦娴脑创a中硕淑,有很多判斷這個(gè)的,這個(gè)是用來判斷異步請(qǐng)求是否已經(jīng)完成的嘉赎。
private AtomicBoolean asyncCompleted = new AtomicBoolean(false);
// 下面就是說的那4個(gè)回調(diào)函數(shù)
// StandardServletAsyncWebRequest
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
}
// StandardServletAsyncWebRequest
@Override
public void onError(AsyncEvent event) throws IOException {
// 調(diào)用在 WebAsyncManager#startCallableProcessing 中添加的處理器
this.exceptionHandlers.forEach(consumer -> consumer.accept(event.getThrowable()));
}
// StandardServletAsyncWebRequest
@Override
public void onTimeout(AsyncEvent event) throws IOException {
// 調(diào)用在 WebAsyncManager#startCallableProcessing 中添加的處理器
this.timeoutHandlers.forEach(Runnable::run);
}
// StandardServletAsyncWebRequest
@Override
public void onComplete(AsyncEvent event) throws IOException {
// 調(diào)用在 WebAsyncManager#startCallableProcessing 中添加的處理器
this.completionHandlers.forEach(Runnable::run);
this.asyncContext = null; // 當(dāng)請(qǐng)求執(zhí)行完成后置媳,這里對(duì) asyncContext 清空以契合之前的邏輯
this.asyncCompleted.set(true); // 重點(diǎn)記憶
}
在了解了這些方法的基礎(chǔ)上進(jìn)行一些較為重要的補(bǔ)充
如果超時(shí),觸發(fā)順序是onTimeout
->onComplete
曹阔。
手動(dòng)調(diào)用AsyncContext#complete()
半开,觸發(fā)的順序是onComplete
。
手動(dòng)調(diào)用AsyncContext#dispatch()
赃份,觸發(fā)的順序是dispatch
->onComplete
寂拆。
這里的dispatch
指的是對(duì)當(dāng)前請(qǐng)求的URI再次調(diào)度。
而且dispatch
發(fā)生時(shí)使用的線程和AsyncListener
回調(diào)使用的線程是同一個(gè)。
記住這些內(nèi)容在理解下文時(shí)很重要。
1.5.5 WebAsyncManager#setConcurrentResultAndDispatch
// WebAsyncManager
private void setConcurrentResultAndDispatch(Object result) {
synchronized (WebAsyncManager.this) {
// concurrentResult 代表最終的處理結(jié)果器予,如果 != RESULT_NONE萌衬,則代表已經(jīng)處理過異步請(qǐng)求結(jié)果了。
if (this.concurrentResult != RESULT_NONE) {
return;
}
this.concurrentResult = result;
}
// 如果異步請(qǐng)求已經(jīng)執(zhí)行完成了的哑子,就直接返回,可以看到它訪問的就是我們上面剛剛看到過的 asyncCompleted。
// 防止對(duì)同一個(gè)請(qǐng)求的結(jié)果進(jìn)行多次處理炭序。
if (this.asyncWebRequest.isAsyncComplete()) {
if (logger.isDebugEnabled()) {
logger.debug("Async result set but request already complete: " + formatRequestUri());
}
return;
}
if (logger.isDebugEnabled()) {
boolean isError = result instanceof Throwable;
logger.debug("Async " + (isError ? "error" : "result set") + ", dispatch to " + formatRequestUri());
}
// 間接調(diào)用 AsyncContext#dispatch 來對(duì)當(dāng)前請(qǐng)求的URI再次調(diào)度 (意思就是會(huì)對(duì)當(dāng)前URI再發(fā)送一個(gè)請(qǐng)求)。
this.asyncWebRequest.dispatch();
}
// StandardServletAsyncWebRequest
public boolean isAsyncComplete() {
return this.asyncCompleted.get();
}
isAsyncComplete
的工作邏輯
由于AsyncContext#dispatch()
會(huì)導(dǎo)致再次調(diào)度當(dāng)前請(qǐng)求的URI苍日,
加上上面也說過的惭聂,再次調(diào)度時(shí)使用的線程與回調(diào)方法時(shí)使用的線程是同一個(gè),
所以asyncCompleted
的值相恃,對(duì)當(dāng)前是準(zhǔn)確有效的辜纲,這就是isAsyncComplete的工作邏輯。
asyncCompleted
表示了當(dāng)前異步請(qǐng)求是否正常執(zhí)行通過,所以在上面很多地方都有使用isAsyncComplete()
進(jìn)行判斷耕腾。
1.5.6 執(zhí)行完成的邏輯
基于上面這些知識(shí)點(diǎn)见剩,這里在梳理一下
處理完成的邏輯
。
第一步:Spring MVC 檢測到某個(gè)請(qǐng)求處理方法的返回值是Callable類型的返回值
扫俺。
第二步:選用CallableMethodReturnValueHandler
作為返回值處理器
苍苞。
第三步:將當(dāng)前請(qǐng)求轉(zhuǎn)為異步模式
,同時(shí)注冊(cè)一個(gè)自定義監(jiān)聽器牵舵。
第四步:在當(dāng)前請(qǐng)求內(nèi)創(chuàng)建一個(gè)線程任務(wù)柒啤,隨后提交給taskExecutor
調(diào)度,然后這個(gè)線程任務(wù)順利執(zhí)行完成畸颅。
第五步:隨著AsyncContext#dispatch()
方法的調(diào)用担巩,Servlet容器會(huì)再次調(diào)度當(dāng)前請(qǐng)求的URI,然后回調(diào)我們監(jiān)聽器內(nèi)部的onComplete(AsyncEvent)
函數(shù)没炒。
第六步:在onComplete(AsyncEvent)
內(nèi)調(diào)用執(zhí)行完成后的處理器
涛癌,執(zhí)行攔截器的afterCompletion
方法。
第四步里的這個(gè)線程任務(wù)內(nèi)主要包含的邏輯有:
1.執(zhí)行請(qǐng)求處理方法返回的 Callable.
2.調(diào)用`AsyncContext#dispatch()`送火,通知Servlet容器對(duì)請(qǐng)求進(jìn)行再調(diào)度拳话。
截至到第四步這里,這些步驟都是在首次請(qǐng)求的線程中進(jìn)行的業(yè)務(wù)邏輯种吸。
從第五步開始弃衍,之后的步驟都是在新的線程內(nèi)完成的。
1.5.7 執(zhí)行超時(shí)的邏輯
基于上面這些知識(shí)點(diǎn)坚俗,這里在梳理一下
超時(shí)的邏輯
镜盯。
第一步:Spring MVC 檢測到某個(gè)請(qǐng)求處理方法的返回值是Callable類型的返回值
。
第二步:選用CallableMethodReturnValueHandler
作為返回值處理器
猖败。
第三步:將當(dāng)前請(qǐng)求轉(zhuǎn)為異步模式
速缆,同時(shí)注冊(cè)一個(gè)自定義監(jiān)聽器。
第四步:在當(dāng)前請(qǐng)求內(nèi)創(chuàng)建一個(gè)線程任務(wù)恩闻,隨后提交給taskExecutor
調(diào)度艺糜,但由于Callable
執(zhí)行過久導(dǎo)致請(qǐng)求超時(shí)。
第五步:Servlet容器會(huì)再次調(diào)度當(dāng)前請(qǐng)求的URI幢尚,然后首先回調(diào)我們監(jiān)聽器內(nèi)部的onTimeout(AsyncEvent)
破停,然后調(diào)用onComplete(AsyncEvent)
。
第六步:在onTimeout(AsyncEvent)
內(nèi)調(diào)用執(zhí)行超時(shí)后的處理器
,在onComplete(AsyncEvent)
內(nèi)調(diào)用執(zhí)行完成后的處理器
尉剩。
執(zhí)行超時(shí)的邏輯 和 執(zhí)行完成的邏輯辱挥,它們的差別點(diǎn)開始于第四步的 Callable 能否在指定時(shí)間內(nèi)執(zhí)行完成。
能完成就代表異步操作已完成边涕;完不成就代表異步操作已超時(shí);
這里主要看下超時(shí)機(jī)制的處理邏輯。
FutureTask API文檔
// WebAsyncManager#startCallableProcessing 代碼片段
Future<?> future = this.taskExecutor.submit(() -> {
result = callable.call();
// ... 線程任務(wù)內(nèi)的邏輯
});
interceptorChain.setTaskFuture(future);
// WebAsyncManager#startCallableProcessing 代碼片段
this.asyncWebRequest.addTimeoutHandler(() -> {
logger.debug("Async request timeout for " + formatRequestUri());
Object result = interceptorChain.triggerAfterTimeout(this.asyncWebRequest, callable);
if (result != CallableProcessingInterceptor.RESULT_NONE) {
setConcurrentResultAndDispatch(result);
}
});
// CallableInterceptorChain
public Object triggerAfterTimeout(NativeWebRequest request, Callable<?> task) {
// 這里會(huì)調(diào)用Future#cancel功蜓,試圖取消任務(wù)的執(zhí)行园爷。
cancelTask();
// 調(diào)用攔截器的超時(shí)方法,對(duì)返回值進(jìn)行組裝式撼,默認(rèn)情況下 result 會(huì)是一個(gè) AsyncRequestTimeoutException 類型的異常童社。
// 可以看 TimeoutCallableProcessingInterceptor 這個(gè)攔截器得知原因。
for (CallableProcessingInterceptor interceptor : this.interceptors) {
try {
Object result = interceptor.handleTimeout(request, task);
if (result == CallableProcessingInterceptor.RESPONSE_HANDLED) {
break;
}
else if (result != CallableProcessingInterceptor.RESULT_NONE) {
return result;
}
}
catch (Throwable ex) {
return ex;
}
}
return CallableProcessingInterceptor.RESULT_NONE;
}
// CallableInterceptorChain
private void cancelTask() {
Future<?> future = this.taskFuture;
if (future != null) {
try {
// 會(huì)嘗試取消這個(gè)任務(wù)的執(zhí)行著隆。
// 沒什么好說的扰楼,已經(jīng)在上面放了 API 文檔,自己看美浦。
future.cancel(true);
}
catch (Throwable ex) {
// Ignore
}
}
}