死磕Spring原始碼-MVC處理HTTP分發請求
MVC處理HTTP分發請求
上面已經完成了HandlerMapping的載入,每一個HandlerMapping持有一系列從URL請求到Controller的對映,這種對映關係通常用一個Map(LinkedHashMap,命名為handlerMap)來持有。
private final Map
某個URL對映到哪個Controller,這部分的配置是在容器對Bean進行依賴注入時發生的,透過Bean的postProcessor來完成,(registerHandler方法)。這樣就為DispatcherServlet的分發奠定了資料基礎。
SimpleUrlHandlerMapping註冊handler的程式碼邏輯
SimpleUrlHandlerMapping.initApplicationContext
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
for (Map.Entry entry : urlMap.entrySet()) {
String url = entry.getKey();
Object handler = entry.getValue();
// 確保url以"/"開頭
if (!url.startsWith("/")) {
url = "/" + url;
}
// 去掉空格
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
}
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
//如果url是“/”,則這個url對映的Controller就是rootHandler
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}//如果url是“/*”,則這個url對映的Controller就是DefaultHandler
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {//如果url是正常的url,設定handlerMap的key和value分別對應url和對應的Controller
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
準備好handlerMap中的資料後,我們來看看handlerMapping如何完成請求的對映處理。
在HandlerMapping中定義了getHandler方法,這個方法會根據我們剛剛初始化得到的handlerMap來獲取與HTTP請求對應的HandlerExecutionChain,它封裝了具體的Controller物件。HandlerExecutionChain有兩個比較重要:攔截器(Interceptor)鏈和handler物件,handler物件就是HTTP對應的Controller,透過攔截器鏈裡的攔截器來對handler物件提供功能的增強。
AbstractHandlerMapping.getHandler
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//獲取request對應的handler
Object handler = getHandlerInternal(request);
//獲取預設的handler
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// 根據名稱取出對應的Handler Bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//把這個handler封裝到HandlerExecutionChain中並加上攔截器
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;
}
**獲取request對應的handler的程式碼邏輯**
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//從request獲取url路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//從HandlerMap中獲取指定url路徑的handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// 如果沒有對應url路徑的handler則返回相應的handler(RootHandler或DefaultHandler)
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
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;
}
**得到handler物件後,把這個handler封裝到HandlerExecutionChain中並加上攔截器**
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
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的封裝工作,為handler對http請求響應做好了準備。
DispatcherServlet是HttpServlet的子類,和其他的HttpServlet一樣,透過doService方法來響應HTTP請求(再呼叫doDispatch)。DispatcherServlet透過getHandler得到一個HandlerExecutionChain後,透過HandlerAdapter來驗證這個handler的合法性(handler instanceof Controller,如果是Controller的物件則返回true,反之返回false),合法後執行handler方法獲取結果資料,這些資料都封裝在ModelAndView中返回給前端進行檢視呈現,對檢視呈現的處理是透過呼叫入口:render方法來實現的。
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.
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技術內幕 深入解析Spring架構與設計原理》
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1600/viewspace-2799202/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring MVC原始碼(二) ----- DispatcherServlet 請求處理流程 面試必問SpringMVC原始碼Servlet面試
- Spring MVC的請求處理邏輯SpringMVC
- Spring MVC框架處理Web請求的基本流程SpringMVC框架Web
- Spring MVC檔案請求處理詳解:MultipartResolverSpringMVC
- Spring MVC 處理一個請求的流程分析SpringMVC
- 死磕Spring原始碼-依賴注入Spring原始碼依賴注入
- springmvc原始碼 ---DispatcherServlet 處理請求SpringMVC原始碼Servlet
- Spring MVC能響應HTTP請求的原因?SpringMVCHTTP
- 處理 HTTP 請求的註解HTTP
- 圖解 Spring:HTTP 請求的處理流程與機制【1】圖解SpringHTTP
- 4、Ktor學習-處理HTTP請求;HTTP
- SpringMVC請求處理過程原始碼簡析SpringMVC原始碼
- ThinkPHP6 原始碼分析之請求處理PHP原始碼
- zookeeper原始碼 — 五、處理寫請求過程原始碼
- 精盡Spring MVC原始碼分析 - 一個請求的旅行過程SpringMVC原始碼
- Laravel 底層是如何處理HTTP請求LaravelHTTP
- Kafka原始碼分析(四) - Server端-請求處理框架Kafka原始碼Server框架
- 死磕 jdk原始碼之HashMap原始碼分析JDK原始碼HashMap
- 直播帶貨原始碼,非同步處理中會處理兩次請求原始碼非同步
- 死磕Spring之AOP篇 - Spring AOP兩種代理物件的攔截處理Spring物件
- Spring MVC原始碼(四) ----- 統一異常處理原理解析SpringMVC原始碼
- java 請求HTTP返回json集合,物件處理方式JavaHTTPJSON物件
- 在 .NET 中使用 Flurl 高效處理Http請求HTTP
- Scrapy原始碼閱讀分析_4_請求處理流程原始碼
- SpringMVC原始碼分析:POST請求中的檔案處理SpringMVC原始碼
- yai 請求預處理指令碼AI指令碼
- 死磕 java併發包之AtomicInteger原始碼分析Java原始碼
- 死磕 java併發包之LongAdder原始碼分析Java原始碼
- spring mvc中獲取請求URLSpringMVC
- Angular 記錄 - Rxjs 完整處理一個 Http 請求AngularJSHTTP
- Angular Universal Application 應該處理 HTTP POST 請求嗎?AngularAPPHTTP
- 死磕Spring之IoC篇 - 單例 Bean 的迴圈依賴處理Spring單例Bean
- ES系列(四):http請求分發框架解析HTTP框架
- spring security:ajax請求的session超時處理SpringSession
- ViewGroup事件分發和處理原始碼分析View事件原始碼
- 死磕 java集合之TreeSet原始碼分析Java原始碼
- 死磕 java集合之WeakHashMap原始碼分析JavaHashMap原始碼
- 死磕 java集合之LinkedList原始碼分析Java原始碼