1. 程式人生 > >spring-webmvc請求處理流程

spring-webmvc請求處理流程

請求處理流程

我們先來看哈DispathcerServlet的繼承關係。

dispacherServlet_class

springmvc是基於servlet來的,web.xml中只有一個DispatcherServlet,
我們知道這其實只是所有請求處理都要經過的一個servlet,這個servlet只是做請求的分發處理,
找到合適的處理器,處理,並返回結果,這個是springmvc主要的實現方式,
現在我們來看這個核心DispatcherServlet的處理流程。
這裡先簡單說一下入口,servlet處理是從service(request,response)方法開始的,
這個方法的實現在Httpservlet中。
這個方法最終會根據請求的method(post,get等)轉到子類FrameServlet中的doPost(),doGet()方法進行處理,
在這些方法中,又會委託給processRequest(request, response)方法進行處理,
這裡就直接從這個方法入手了,前面的啥service(),doPost()等的就自己去瞧了。

processRequest(request, response)

//FrameworkServlet.java
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//為了保證當前線層的LocalContext以及RequestAttributes可以再當前請求後還能恢復,提取當前執行緒的兩個屬性(需要點進去看,buildLocaleContext()方法中)
    	//根據當前request建立對應的LocaleContext和requestAttibutes,並繫結到當前執行緒
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
		...
		try {
            //委託給doSerivce()做進一步處理
			doService(request, response);
		}
    	...
		finally {
            //請求結束後恢復執行緒原始狀態
			resetContextHolders(request, previousLocaleContext, previousAttributes);
            //請求處理結束後無論成功與否,釋出事件通知
			...
			publishRequestHandledEvent(request, startTime, failureCause);
		}
}
從FrameworkServlet的processRequest()方法中,我們可以看到以下流程(都在註釋中):
1. 為了保證當前線層的LocalContext以及RequestAttributes可以再當前請求後還能恢復,提取當前執行緒的兩個屬性;
2. 根據當前request建立對應的LocaleContext和requestAttibutes,並繫結到當前執行緒;
3. 委託給doSerivce()做進一步處理;
4. 請求結束後恢復執行緒原始狀態;
5. 請求處理結束後無論成功與否,釋出事件通知。

doService(request, response)

這個時候,我們來到了DispacherServlet的doService()方法。

//DispacherServlet.java
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...
    try {
        	//請求分發處理
			doDispatch(request, response);
		}
    ...
}

根據spring一貫的風格,這個時候的doservice依然是在做一些準備工作,準備工作中spring會把我們初始化的各種resolver和WebapplicationContext放到request的Attribute中,以便之後使用。重點來到doDispatch(request, response);

//DispacherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		...
         HandlerExecutionChain mappedHandler = null;
         //如果是檔案上傳使用的MultipartContent型別的request,則將request轉換成MultipartHtptServletRequest型別的request
		processedRequest = checkMultipart(request);
    	...
		// 根據request資訊尋找對應的handler,
		mappedHandler = getHandler(processedRequest, false);
		if (mappedHandler == null || mappedHandler.getHandler() == null) {
            //如果沒有找到對應的handler,則通過response返回錯誤資訊
			noHandlerFound(processedRequest, response);
			return;
		}
		// 通過找到的handler,尋找它的介面卡handlerAdapter
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    	...
		// 呼叫攔截器的preHandle方法
		if (!mappedHandler.applyPreHandle(processedRequest, response)) {
			return;
		}
		try {
			// 真正的啟用handler呼叫,並返回檢視
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
		}
    	...
		//檢視名稱的轉換,用於需要新增前後綴的情況
        applyDefaultViewName(request, mv);
		// 呼叫攔截器的postHandle方法
    	mappedHandler.applyPostHandle(processedRequest, response, mv);
		...
         //處理結果
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		...
}

getHandler

gethandler最終會呼叫到如下方法,可以看到其實就是遍歷我們初始化的所有handlerMappings來尋找一個合適的handler並返回。

// DispacherServlet.java
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			...
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

以我們最熟悉的@Controller註解方式來分析,系統會註冊預設的requestMapping,其中包含DefaultAnnotationHandlerMapping,一看就知道這個是處理註解對映的,不過這個類在3.2.x版本之後就棄用了,該用RequestMappingHandlerMapping,我們進入hm.getHandler(request)檢視,這個方法來自於超類AbstractHandlerMapping。

//AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//根據request獲取對應的handler,由子類AbstractUrlHandlerMapping實現
		Object handler = getHandlerInternal(request);
		if (handler == null) {
            //如果沒有handler,則使用預設的handler
			handler = getDefaultHandler();
		}
    	//如果也沒有預設的handler,則不繼續處理,返回null
		if (handler == null) {
			return null;
		}
		//如果handler是一個string型別的,那說明這是個bean的name,根據它去獲取對應的bean
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}
    	//加入攔截器,並返回添加了攔截器的handler處理鏈
		return getHandlerExecutionChain(handler, request);
}
//AbstractUrlHandlerMapping.java
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    	//查詢對應的handler
		Object handler = lookupHandler(lookupPath, request);
		...
		return handler;
	}

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		//根據urlPath找到對應的handler,其實就是我們的Controller
		Object handler = this.handlerMap.get(urlPath);
    	if (handler != null) {
			...
             //新增Inteceptor並封裝成HandlerExecutionChain並返回
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		}
		...
}

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
			String pathWithinMapping, Map<String, String> uriTemplateVariables) {
		//將handler封裝到HandlerExecutionChain中
		HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
    	//新增Interceptor
		chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
		if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
			chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
		}
		return chain;
	}
如下圖,我們可以看到在lookupHandler()方法中的handlerMap其實就是url->controller的對映。

controller_handler_1

看下圖,我們可以看到,lookupHandler返回了封裝好的HandlerExcutionChain,該例項的屬性handler是之前找到的controller。

controller_handler

getHandlerAdapter

getHandlerAdapter也是遍歷之前註冊的所有adapter來獲取對應的handler的adapter,注意入參,是handler,就是我們的controller。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			...
			if (ha.supports(handler)) {
				return ha;
			}
		}
    	...
}

直接上圖,可以看到handlerAdapters有三個,我們使用的是@Controller註解的方式,我們直接來看AnnotationMethodHandlerAdapter。

handler_adapter_1

//AnnotationMethodHandlerAdapter.java
public boolean supports(Object handler) {
    //其實就是找一個resolver,如果沒有初始化一個,然後判斷一下handler裡面的method是否為空
    //因為我們是註解的方式,能夠想到方法呼叫是採用反射的方式,所以這裡會判斷method是否為空
	return getMethodResolver(handler).hasHandlerMethods();
}

ha.handle()

這個ha是上一步獲取到的handlerAdapter,不是第一步獲取的的handler哦,這裡才真正開始執行呼叫我們的controller邏輯。

//AnnotationMethodHandlerAdapter.java
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
	...
    //一貫風格又來了
    return invokeHandlerMethod(request, response, handler);
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	...
         //呼叫我們的controller的方法,返回結果
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    	//生成一個檢視,會根據返回值的型別,是否加了ResponseBody註解做不同的處理,感興趣的自己去看
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		...
		return mav;
	}
可以看到我們的controller最終被呼叫了,
spring在呼叫返回結果後會對結果的返回值型別,方法是否加了@ResponseBody註解等做進一步處理,這部份就自己去看了哦,
這裡說明一下,如果方法加了@ResponseBody註解,那麼返回的mav=null。

processDispatchResult()

在這個的上一步會執行Interceptor的postHandle()方法mappedHandler.applyPostHandle(processedRequest, response, mv);,這裡提一下。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
                //呼叫HandlerExceptionResolver處理異常,
                //這個東西就是在servlet初始化的時候最後一步initStrategies()方法當中,跟requestMapping等一起註冊的
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}
    	if (mv != null && !mv.wasCleared()) {
            //如果返回的mv不為空,處理檢視,並跳轉,這裡會用到viewResolver
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		...
		if (mappedHandler != null) {
            //呼叫Intecepter的afterCompletion()方法
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
在最後的處理結果當中,如果請求有異常發生,就會導向異常處理器去處理,
如果沒有,就會根據是否存在檢視進行跳轉,最後呼叫Interceptor的afterCompletion()方法。
到這裡整個請求算完了,剩下的就是一些後續清理工作,釋出時間通知等工作了。