Spring源碼解析之SpringMVC源碼解析(二)


Spring源碼解析之SpringMVC源碼解析(二)

在 中,分析了SpringBoot中SpringMVC的自動配置原理以及DispatcherServlet的初始化流程。本篇文章就分析一次請求在SpringMVC中的處理流程

在日常開發中,我們最常用的請求方式大概就是Get和Post了,當然也有put和delete,Tomcat或者Jetty等web服務器在接受到請求後會調用到DispatcherServlet對應的方法

<code>/**
* Delegate GET requests to processRequest/doService.
*

Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

/**
* Delegate PUT requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);


}

/**
* Delegate DELETE requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

/<code>

可以看到其實最終都是調用的同一個方法

<code>/**
* Process this request, publishing an event regardless of the outcome.
*

The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//記錄當前線程的信息
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);

RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

initContextHolders(request, localeContext, requestAttributes);

try {
//核心處理,往下看 重點
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;


}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}

finally {
//清除線程綁定信息
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
//發送事件通知
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

/**
\t * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
\t * for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
\t\tlogRequest(request);

\t\t// Keep a snapshot of the request attributes in case of an include,
\t\t// to be able to restore the original attributes after the include.
\t\tMap<string> attributesSnapshot = null;
\t\tif (WebUtils.isIncludeRequest(request)) {
\t\t\tattributesSnapshot = new HashMap<>();
\t\t\tEnumeration> attrNames = request.getAttributeNames();
\t\t\twhile (attrNames.hasMoreElements()) {
\t\t\t\tString attrName = (String) attrNames.nextElement();
\t\t\t\tif (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
\t\t\t\t\tattributesSnapshot.put(attrName, request.getAttribute(attrName));
\t\t\t\t}
\t\t\t}
\t\t}

\t\t// Make framework objects available to handlers and view objects.
\t\trequest.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
\t\trequest.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
\t\trequest.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
\t\trequest.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

\t\tif (this.flashMapManager != null) {
\t\t\tFlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
\t\t\tif (inputFlashMap != null) {

\t\t\t\trequest.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
\t\t\t}
\t\t\trequest.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
\t\t\trequest.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
\t\t}

\t\ttry {
//核心流程代碼往下看
\t\t\tdoDispatch(request, response);
\t\t}
\t\tfinally {
\t\t\tif (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
\t\t\t\t// Restore the original attribute snapshot, in case of an include.
\t\t\t\tif (attributesSnapshot != null) {
\t\t\t\t\trestoreAttributesAfterInclude(request, attributesSnapshot);
\t\t\t\t}
\t\t\t}
\t\t}
\t}
/<string>

/<code>

可以看到上方的大段代碼都是做的一些準備工作,具體的邏輯接著往下看吧,這個核心流程都在下面這個方法裡了

其中標註了序號的重點關注

<code>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 {
//如果是文件上傳請求則進行特殊處理
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
\t//1.為當前請求獲取對應的handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//如果沒有獲取到對應的handler則往response中寫入錯誤信息
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
//2.為當前請求 獲取對應的handlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
//處理last-modified情況
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

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

// Actually invoke the handler.
//3.調用handle
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
\t\t\t\t//如果函數調用沒有返回視圖則使用默認的
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);
}
//4. 處理返回結果
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);
}
}
}
}
/<code>
1. 獲取handler
<code>protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
/<code>
<code>public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//1.1 調用具體的實現去獲取handler
Object handler = getHandlerInternal(request);

if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
//嘗試通過BeanName去獲取handler
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
\t//1.2 獲取handler執行鏈
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)) {
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;
}
/<code>
1.1 調用具體的實現去獲取handler

這裡可以看到咱們HandlerMapping 實現就是用策略模式實現的,瞭解策略模式

這裡以AbstractUrlHandlerMapping為例解讀一下,顧明思議,這個類是根據請求url獲取響應的handler的

<code>protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
///截取url

String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//根據url尋找handler 往下看
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
// 如果請求路徑為/則使用RootHandler
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
// 根據beanName嘗試獲取Handler
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
/<code>
<code>protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
//直接根據url匹配
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
//封裝執行鏈 重點往下看
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}

// Pattern match?
List<string> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {

if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}

String bestMatch = null;
Comparator<string> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<string> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<string> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<string> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);

}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}

// No handler found...
return null;
}
/<string>/<string>/<string>/<string>/<string>/<code>
1.2封裝執行鏈

當獲取到相應的handler後,查看是否存在攔截器,如果存在的話則加入執行鏈中

<code>protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<string> uriTemplateVariables) {

HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
/<string>/<code>
2. 獲取handlerAdpter

根據handler獲取匹配的handlerAdpter

HandlerAdapter作用:當DispatcherServlet類中想使用現成的Handler類的時候,發現Handler類型不統一,這裡使用適配模式 適配成統一的HandlerAdapter類型。

<code>protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
//不同的handlerAdapter的判斷方法不同
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");
}

//以SimpleControllerHandlerAdapter為例,判斷是否實現Controller接口
public class SimpleControllerHandlerAdapter implements HandlerAdapter {

\t@Override
\tpublic boolean supports(Object handler) {
\t\treturn (handler instanceof Controller);
\t}
//執行請求
\t@Override
\t@Nullable
\tpublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
\t\t\tthrows Exception {
\t\t//3.執行請求方法往下看
\t\treturn ((Controller) handler).handleRequest(request, response);
\t}

\t@Override
\tpublic long getLastModified(HttpServletRequest request, Object handler) {
\t\tif (handler instanceof LastModified) {
\t\t\treturn ((LastModified) handler).getLastModified(request);
\t\t}
\t\treturn -1L;
\t}
}
/<code>
3. 執行請求
<code>public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {

if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}

// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);

// Execute handleRequestInternal in synchronized block if required.
// 如果需要同步session
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);

if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
\t //調用Controller方法 返回ModelAndView
return handleRequestInternal(request, response);
}
/<code>
4. 處理返回結果
<code>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) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
/<code>

視圖處理邏輯

<code>protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);

View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
//4.1 解析視圖名
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}

// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//4.2 視圖跳轉
view.render(mv.getModelInternal(), request, response);

}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
/<code>
4.1 解析視圖名稱
<code>protected View resolveViewName(String viewName, @Nullable Map<string> model,
Locale locale, HttpServletRequest request) throws Exception {

if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
/<string>/<code>
4.2 視圖跳轉(頁面跳轉)
具體的跳轉邏輯是根據當前使用的渲染引擎決定的,比如html、jsp、Thymeleaf等。


分享到:


相關文章: