Spring mvc 從一個http請求分析DispatcherServlet的工作過程
開發工具 Intellij IDEA
目標、除錯 請求http://localhost:8080/coffee/helloworld
url-pattern 攔截所有的/coffee開頭的請求<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/coffee</url-pattern> </servlet-mapping>
@Controller
@RequestMapping("coffee")
public class HelloControler{
@RequestMapping("helloworld")
public @ResponseBody String helloWorld() {
System.out.println("xxx");
return "hello 222 world";
}
}
<pre style="font-family: 宋體; font-size: 12pt; background-color: rgb(255, 255, 255);"><pre name="code" class="java">/** * Delegate GET requests to processRequest/doService. * <p>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); }
org.springframework.web.servlet.FrameworkServlet
/** * Process this request, publishing an event regardless of the outcome. * <p>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);//******* doService *********// } catch (ServletException ex) { failureCause = ex; throw ex; } catch (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(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } }
接著看一下 DispatcherServlet的方法
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// 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;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);//********************************///
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
然後看一下 最關鍵的方法/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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);
// **********************************根據request查詢mappedHandler 也就是一個Controller
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
看一下Spring如何匹配url 然後查詢對應的Controller/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
private List<HandlerMapping> handlerMappings;
handlerMappings 是包含倆元素
BeanNameUrlHandlerMapping
DefaultAnnotationHandlerMapping
package org.springframework.web.servlet.handler;
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
package org.springframework.web.servlet.handler;
/**
* Look up a handler for the URL path of the given request.
* @param request current HTTP request
* @return the handler instance, or {@code null} if none found
*/
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);//coffee/helloworld *** 往下看
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.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
package org.springframework.web.util;
class UrlPathHelper public String getLookupPathForRequest(HttpServletRequest request) {
if(this.alwaysUseFullPath) {
return this.getPathWithinApplication(request);
} else {
String rest = this.getPathWithinServletMapping(request);//執行結果 --> ""
return !"".equals(rest)?rest:this.getPathWithinApplication(request);//執行getpathWithApplication 返回 /coffee/helloworld
}
}
上面的方法執行的是elsepublic String getPathWithinServletMapping(HttpServletRequest request) {
String pathWithinApp = this.getPathWithinApplication(request);//coffee/helloworld ******往下看
String servletPath = this.getServletPath(request);<span style="font-family: 宋體;">//coffee/helloworld </span><span style="font-family: 宋體;">******往下看</span>
String sanitizedPathWithinApp = this.getSanitizedPath(pathWithinApp);
String path;
if(servletPath.contains(sanitizedPathWithinApp)) {
path = this.getRemainingPath(sanitizedPathWithinApp, servletPath, false); //結果 ""
} else {
path = this.getRemainingPath(pathWithinApp, servletPath, false);
}
if(path != null) {
return path; // 返回 ""
} else {
String pathInfo = request.getPathInfo();
if(pathInfo != null) {
return pathInfo;
} else {
if(!this.urlDecode) {
path = this.getRemainingPath(this.decodeInternal(request, pathWithinApp), servletPath, false);
if(path != null) {
return pathWithinApp;
}
}
return servletPath;
}
}
}
public String getPathWithinApplication(HttpServletRequest request) {
String contextPath = this.getContextPath(request);// ""
String requestUri = this.getRequestUri(request);//coffee/helloworld
String path = this.getRemainingPath(requestUri, contextPath, true);//coffee/helloworld
return path != null?(StringUtils.hasText(path)?path:"/"):requestUri;//coffee/helloworld
}
public String getContextPath(HttpServletRequest request) {
String contextPath = (String)request.getAttribute("javax.servlet.include.context_path");
if(contextPath == null) {
contextPath = request.getContextPath();
}
if("/".equals(contextPath)) {///*****注意一下這個方法如果
contextPath = "";
}
return this.decodeRequestString(request, contextPath);
}
public String getServletPath(HttpServletRequest request) {
String servletPath = (String)request.getAttribute("javax.servlet.include.servlet_path");
if(servletPath == null) {
servletPath = request.getServletPath();//*******/coffee/helloworld
}
if(servletPath.length() > 1 && servletPath.endsWith("/") && this.shouldRemoveTrailingServletPathSlash(request)) {
servletPath = servletPath.substring(0, servletPath.length() - 1);
}
return servletPath;
}
/**
* Look up a handler instance for the given URL path.
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
* and various Ant-style pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the AntPathMatcher class.
* <p>Looks for the most exact pattern, where most exact is defined as
* the longest path pattern.
* @param urlPath URL the bean is mapped to
* @param request current HTTP request (to expose the path within the mapping to)
* @return the associated handler instance, or {@code null} if not found
* @see #exposePathWithinMapping
* @see org.springframework.util.AntPathMatcher
*/
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match? *************************************下面一行程式碼是真正查詢Controller的方法
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
List<String> matchingPatterns = new ArrayList<String>();
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 bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestPatternMatch = matchingPatterns.get(0);
}
if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
if (handler == null) {
Assert.isTrue(bestPatternMatch.endsWith("/"));
handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
相關推薦
Spring mvc 從一個http請求分析DispatcherServlet的工作過程
開發工具 Intellij IDEA 目標、除錯 請求http://localhost:8080/coffee/helloworld <servlet> <servlet-name>dispatcher</servlet-
基於Spring MVC框架的Http流程分析
一、問題提出 我們可以方便的利用Spring MVC進行業務開發,請求的大部分工作都被框架和容器封裝,使得我們只需要做很少量的
Spring MVC 處理一個請求的流程分析
Spring MVC是Spring系列框架中使用頻率最高的部分。不管是Spring Boot還是傳統的Spring專案,只要是Web專案都會使用到Spring MVC部分。因此程式設計師一定要熟練掌握MVC部分。本篇部落格簡要分析Spring MVC處理一個請求的流程。 一個請求從客戶端發出到達伺服器,然後
Spring MVC @RestController接收POST請求,用Map接收
@requestbody 微服務 springboot springmvc背景脫離傳統項目,使用微服務集群開發後。RestFul風格勢在必行,所以,本篇文章針對使用restful風格的編寫人員。主題restful post請求提交的參數用什麽接收?關鍵字Map、Entity內容1、首先,傳統的接收使用Http
spring mvc從@ResponseBody取到json發現中文亂碼
tab reat builder attr cover proc first hresult acc 問題背景:如題。 問題定位:代碼跟蹤,從源頭入手,一步一步跟進,直到設置中文編碼的地方。 問題代碼: /** * 獲取單個測試樁接口內容
Spring MVC 從 Controller向頁面傳值的方式
用戶 () 傳參數 control let att model enter 設定 Spring MVC 從 Controller向頁面傳值的方式 在實際開發中,Controller取得數據(可以在Controller中處理,當然也可以來源於業務邏輯層),傳給頁面,常用的方
Spring MVC - 02 RequestMapping對映請求
使用 @RequestMapping 對映請求 1.SpringMVC 使用@RequestMapping 註解為 控制器 指定可以處理哪些URL 請求 2. 在控制器的 類定義 及 方法定義處 都可以標註@RequestMapping 類定義處: 提供初步的請求對映資訊。 相對於 WEB 應用的根目錄
從一個線上問題分析binlog與內部XA事務提交過程
引入 實例 only 定義 api bug 功能 觸發 技術分享 1. 問題業務上新增一條訂單記錄,用戶接收到BinLake拉取的MySQL從庫數據消息後,馬上根據消息內的訂單號去查詢同一個MySQL從庫,發現有些時候無法查到該條數據,等待大約500ms~1000ms
ASP.NET/MVC/Core的HTTP請求流程
ASP.NET HTTP管道(Pipeline)模型 1. 先講一點,再深刻思考 一般我們都在寫業務程式碼,優化頁面,優化邏輯之間內徘徊。也許我們懂得HTTP,HTTPS的GET,POST,但是我們大部分人是不知道ASP是如何去解析HTTP,或者IIS是如何去處理頁面請求。我們只知道WebForm拉控制元
Spring MVC 資料繫結流程分析
1. 資料繫結流程原理★ ① Spring MVC 主框架將 ServletRequest 物件及目標方法的入參例項傳遞給 WebDataBinderFactory 例項,以建立 DataBinder 例項物件 ②
vue + axios---封裝一個http請求
在使用vue開發時,官方推薦使用axios來請求介面 // axios官方地址 https://github.com/axios/axios 但是axios並不像 vue-resource 一樣擁有install,即不能直接 Vue.use(axios) 來使用,所以需要我們自己根據axios來寫
如何判斷一個HTTP請求是瀏覽器請求還是應用程式請求?
1、獲取請求的request HttpServletRequest request=ServletActionContext.getRequest(); 2、攔截器中判斷請求頭 通常判斷來自手機端的請求還是PC端的請求只需要判斷: request.getHea
Spring MVC : 從Model和View名稱生成最終的View
當我們使用 Spring MVC開發Web應用頁面時,一般會使用某種檢視模版引擎技術,比如FreeMarker,Velocity之類的,然後還會寫很多控制器方法用來處理某個請求,這些控制器方法基本的套路是: 寫一個檢視模板,配置到合適的位置; 控制器方法接收處
容器完整處理一個http請求的過程
初學java web的朋友們應該都知道tomcat容器,但是tomcat是如何完成一次http請求的過程,這裡做一個記錄。 當用戶在客戶端點選一個連結,該連結的URL指向一個servlet,經過網路轉發到應用所在的web伺服器的,此時web伺服器不是直接把申請發給servlet本身,而是傳送給部署該s
一個http請求的詳細過程
首先http是一個應用層的協議,在這個層的協議,只是一種通訊規範,也就是因為雙方要進行通訊,大家要事先約定一個規範。 1.連線 當我們輸入這樣一個請求時,首先要建立一個socket連線,因為socket是通過ip和埠建立的,所以之前還有一個DNS解析過程,把www.m
Spring mvc 從零搭建
以eclipse 為例一:new Dynamic Web Project(如果沒有web.xml,輸入完專案名點下一步,勾選Generate web.xml deployment descriptor)二:在WebContent 寫一個靜態頁面 hello.html(試試we
SpringMVC:處理一個http請求的完整過程
SpringMVC是一個基於DispatcherServlet的MVC框架,每一個請求最先訪問的都是DispatcherServlet,DispatcherServlet負責轉發每一個Request請求給相應的Handler,Handler處理以後再返回相
Tomcat處理一個HTTP請求的過程
一、Tomcat的組成 (1)Server伺服器元素代表整個catalina servlet容器。是單例模式。 (2)ServiceService是這樣一個集合:它由一個或者多個Connector組成,以及一個Engine,負責處理所有Connector所獲得的客戶請求。
一個HTTP請求例項
一個 HTTP 請求例項12當用戶點選回車按鈕,瀏覽器將頁面的請求通過網路傳送到 Web 伺服器。3Web 伺服器接收請求並解析請求資訊。在 Web 伺服器的配置檔案中有配置當前專案根目錄路徑。由於當前訪問的 URL 地址不包含子路徑,Web 伺服器會查詢配置檔案的 inde
Jmeter:Regular Expression Extractor正則表示式提取器上一個http請求報文內容作為下一個請求的引數
正則表示式提取器說明 新增元件 配置 引用 下面說明是參考網上的文章 說明: (1)引用名稱:下一個請求要引用的引數名稱,如填寫title,則可用${title}引用它。 (2)正則表示式: ():括起來的部分就是要提取的。