1. 程式人生 > >SpringMVC原始碼分析(二)之請求如何轉發到對應的Controller

SpringMVC原始碼分析(二)之請求如何轉發到對應的Controller

        在前一篇對DispatcherServlet的分析中,初略的過了下請求是如何處理的,本文將重點分析,HandlerMapping與HandlerAdapter是如何工作的

         在web容器啟動的過程中,會初初始化一系列SpringMVC所需的類,這裡我們看看AbstractHandlerMethodMapping類的initHandlerMethods方法


          如果bean中有相應註解  檢測HandlerMethod

    

          具體看看 selectMethods方法的實現

而其中的inspect方法  呼叫了 getMappingForMethod方法  具體實現 由其子類 RequestMappingHandlerMapping實現

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
		}
		return info;
	}

此方法返回的是一個RequestMappingInfo類例項,可見我們之前methods Map中的T 型別就為RequestMappingInfo

此類是一個封裝了各種請求對映條件並實現了RequestCondition介面的類。有各種RequestCondition實現類屬性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,這個請
求條件看屬性名也瞭解,分別代表http請求的路徑模式、方法、引數、頭部等資訊。

繼續看getMappingForMethod方法,createRequestMappingInfo的具體實現如下:

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		RequestCondition<?> condition = (element instanceof Class<?> ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}
首先找到方法的@RequestMapping註解,然後獲取RequestCondition ,如果有註解,則呼叫具體建立方法,否則返回null
protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, RequestCondition<?> customCondition) {

		return RequestMappingInfo
				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
				.methods(requestMapping.method())
				.params(requestMapping.params())
				.headers(requestMapping.headers())
				.consumes(requestMapping.consumes())
				.produces(requestMapping.produces())
				.mappingName(requestMapping.name())
				.customCondition(customCondition)
				.options(this.config)
				.build();
	}
除了pathCondithion 其他都是直接使用 requestMapping註解中的condition

回頭再看registerHandlerMethod方法


到此 就註冊完URL相關操作

回頭再看 doDispatch方法中 getHandler的呼叫 最終會呼叫getHandlerInternal方法,具體實現:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
關鍵程式碼是通過呼叫 lookupHandlerMethod獲取 handlerMethod


以上都是基於Controller方法的對映 RequestMappingHandlerMapping,關於靜態資源的對映 這裡就不作詳細說明了,對應的handlerMpping 為SimpleUrlHandlerMapping

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
通過handler 和 request構建 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;
	}

HandlerAdapter的獲取以及是如何工作的

       同樣在doDispatch中 我們通過handler來獲取handlerAdapter

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   for (HandlerAdapter ha : this.handlerAdapters) {
      if (logger.isTraceEnabled()) {
         logger.trace("Testing handler adapter [" + ha + "]");
      }
      if (ha.supports(handler)) {
         return ha;
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

這裡使用的handlerAdapters是在private void initHandlerAdapters(ApplicationContext context)方法中得到初始化的,大致思想就是將所有實現了HandlerAdapter介面的類的例項都找出來放到這個list中
我們以RequestMappingHandlerAdapter 例項來分析 對應的supports方法為 父類 AbstractHandlerMethodAdapter 方法:

public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}
 在RequestMappingHandlerAdapter實現 supportsInternal:
protected boolean supportsInternal(HandlerMethod handlerMethod) {
		return true;
	}

可以發現RequestMappingHandlerAdapter 是對所有handlerMethod 進行適配呼叫

回到doDispatch

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

handler的呼叫  核心方法handleInternal的實現為:

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		checkRequest(request);

		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandlerMethod(request, response, handlerMethod);
				}
			}
		}

		return invokeHandlerMethod(request, response, handlerMethod);
	}

內容很多,我們只需要重點關心invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);

		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		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);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}

		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}

在此方法中,會根據 handlerMethod 建立ServletInvocableHandlerMethod 物件,設定方法執行過程中相應的 resovler

然後呼叫 invokeAndHandle:

public void invokeAndHandle(ServletWebRequest webRequest,
			ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
                   //執行請求 並返回結果
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(this.responseReason)) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
			}
			throw ex;
		}
	}
可以看到 第一行呼叫 invokeForRequest執行請求
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
					"' with arguments " + Arrays.toString(args));
		}
		Object returnValue = doInvoke(args);
		if (logger.isTraceEnabled()) {
			logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
					"] returned [" + returnValue + "]");
		}
		return returnValue;
	}

繼續跟doInvoke方法,對於上面的請求引數解析下一篇部落格會分析,本文只分析方法的呼叫


其中的getBridgedMethod為:

protected Method getBridgedMethod() {
		return this.bridgedMethod;
	}
而我們的bridgedMethod是什麼呢,不知道大家有沒有留意到,在前面我提到過的 ServletInvocableHandlerMethod 例項的建立
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

而createInvocableHandlerMethod 就是好呼叫ServletInvocableHandlerMethod  HandlerMethod引數 建構函式,最終會呼叫HandlerMethod建構函式:
protected HandlerMethod(HandlerMethod handlerMethod) {
		Assert.notNull(handlerMethod, "HandlerMethod is required");
		this.bean = handlerMethod.bean;
		this.beanFactory = handlerMethod.beanFactory;
		this.beanType = handlerMethod.beanType;
		this.method = handlerMethod.method;
		this.bridgedMethod = handlerMethod.bridgedMethod;
		this.parameters = handlerMethod.parameters;
		this.responseStatus = handlerMethod.responseStatus;
		this.responseStatusReason = handlerMethod.responseStatusReason;
		this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
	}

其中就有對 bridgedMethod 的賦值,這下就應該清楚了  adapter是如何來呼叫我們Controller的方法啦

本節重點分析了 RequestMappingHandlerMapping 與 RequestMappingHandlerAdapter的工作原理以及過程

相關推薦

SpringMVC原始碼分析請求如何轉發對應Controller

        在前一篇對DispatcherServlet的分析中,初略的過了下請求是如何處理的,本文將重點分析,HandlerMapping與HandlerAdapter是如何工作的          在web容器啟動的過程中,會初初始化一系列SpringMVC所需

tornado原始碼分析iostream

在事件驅動模型中,所有任務都是以某個事件的回撥函式的方式新增至事件迴圈中的,如:HTTPServer要從socket中讀取客戶端傳送的request訊息,就必須將該socket新增至ioloop中,並設定回掉函式,在回掉函式中從socket中讀取資料,並且檢查request訊息是否全部接收到了,如果

Docker原始碼分析Docker Client

一、建立Docker Client     Docker是一個client/server的架構,通過二進位制檔案docker建立Docker客戶端將請求型別與引數傳送給Docker Server,Docker Server具體執行命令呼叫。 Docker Client執行流

SpringMVC原始碼分析-URL對映的註冊

​ 首先我們來看下AbstractHandlerMethodMapping這個類,它實現了InitializingBean介面,裡面有個afterPropertiesSet()方法。這個介面是spring-beans這個元件的內容,想一想,平時使用搭建Spr

Glide原始碼分析——從用法來看load&into方法

上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260 在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques

zigbee ZStack-2.5.1a原始碼分析 無線接收控制LED

本文描述ZStack-2.5.1a 模板及無線接收移植相關內容。 main HAL_BOARD_INIT // HAL_TURN_OFF_LED1 InitBoard HalDriverInit HalAdcInit

Java多執行緒ReentrantLock實現原理和原始碼分析

章節概覽、 1、ReentrantLock概述 ReentrantLock字面含義是可重入的互斥鎖,實現了和synchronize關鍵字一樣的獨佔鎖功能。但是ReentrantLock使用的是自旋鎖,通過CAS硬體原語指令實現的輕量級的鎖,不會引起上下文切換

Java併發AQS原始碼分析

我在Java併發之AQS原始碼分析(一)這篇文章中,從原始碼的角度深度剖析了 AQS 獨佔鎖模式下的獲取鎖與釋放鎖的邏輯,如果你把

Tomcat原始碼分析 ----- HTTP請求處理過程

我們接著上一篇文章的容器處理來講,當postParseRequest方法返回true時,則由容器繼續處理,在service方法中有connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)這一

Flume NG原始碼分析支援執行時動態修改配置的配置模組

在上一篇中講了Flume NG配置模組基本的介面的類,PropertiesConfigurationProvider提供了基於properties配置檔案的靜態配置的能力,這篇細說一下PollingPropertiesFileConfigurationProvider提供的執行時動態修改配置並生效的

GCC原始碼分析——前端

原文連結:http://blog.csdn.net/sonicling/article/details/6706152   從這一篇開始,我們將從原始碼的角度來分析GCC如何完成對C語言原始檔的處理。GCC的內部構架在GCC Internals(搜“gccint.pdf”,或者見[

YOLOv2原始碼分析

文章全部YOLOv2原始碼分析 接著上一講沒有講完的make_convolutional_layer函式 0x01 make_convolutional_layer //make_convolutional_laye

兄弟連區塊鏈入門教程eth原始碼分析p2p-udp.go原始碼分析

ping方法與pending的處理,之前談到了pending是等待一個reply。 這裡通過程式碼來分析是如何實現等待reply的。pending方法把pending結構體傳送給addpending. 然後等待訊息的處理和接收。 // ping sends a ping message to the giv

Spring原始碼分析IoC容器的實現1

    Ioc(Inversion of Control)——“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。理解好Ioc的關鍵是要明確“誰控制誰,控制什麼,為何是反轉(有

Cat原始碼分析:Server端

初始化 服務端消費客戶端發來的訊息進行分析和展示,所以這個的初始化指的是CatHomeModule的初始化 CatHomeModule依賴TcpSocketReceiver和MessageConsumer,前者用來接收客戶端傳送的訊息,後者用來消費訊息。 TcpSocket

subsampling-scale-image-view載入長圖原始碼分析

subsampling-scale-image-view原始碼分析概要分析總結 概要 subsampling-scale-image-view是一個支援部分載入大圖長圖的圖片庫,並且還支援縮放,在subsampling-scale-image-view載入長圖原

比特幣原始碼研讀搭環境遇到的那些坑

首先說一下度《精通比特幣》是一直不理解的一個地方: 上面兩處被圈起來的地方都提到了一個數字2256,特別是第一句話更是讓人費解,如果私鑰只能在1到2256間產生那豈不是太容易重複了。關於這點,我認為是在翻譯或者排版是出現了錯誤,2256應該是想表達2的256次方的意

Spring component-scan原始碼分析 -- @Configuration註解處理

上篇文章Spring component-scan原始碼分析(一) – XML解析分析了Spring解析<context:component-scan …/>標籤時,把掃描到的合適的類封裝成BeanDefinition加入Sping容器中,本篇分析S

Spring原始碼分析IoC容器的實現3

BeanDefinition的載入和解析     這個載入過程,相當於把定義的BeanDefinition在IoC容器中轉化成一個Spring內部表示的資料結構的過程。IoC容器對Bean的管理和依賴注入功能的實現,是通過對其持有的BeanDefinition進

Spring原始碼分析IoC容器的實現2

IoC容器的初始化過程     簡單來說IoC容器的初始化是由refresh()方法啟動的,這個方法標誌著IoC容器的正式啟動。這個啟動包括BeanDefinition的Resouce定位、載入和註冊三個基本過程。     第一