springmvc path請求映射到bean 方法的流程

一、加載註冊流程

1.在dispatch-servlet.xml中配置< mvc:annotation-driven/>,在控制器的方法上加入@RequestMapping註解即可。

2.mvc:annotation-driven的解析流程 會調用到自定義元素解析器的AnnotationDrivenBeanDefinitionParser.parse方法。

3.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping為RequestMapping註解映射到後臺接口的註冊表。此類實現瞭InitializingBean接口,會觸發到

afterPropertiesSet方法。

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
 
@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
 
	/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #getCandidateBeanNames()
	 * @see #processCandidateBean
	 * @see #handlerMethodsInitialized
	 */
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

4.在initHandlerMethods方法中會先調用getCandidateBeanNames獲取當前容器工廠的所有BEAN,然後逐個BEAN進行處理。

4.1 獲取所有BEAN流程

protected String[] getCandidateBeanNames() {
		return (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));
	}

4.2 處理BEAN流程

protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

4.3 判斷當前BEAN是否HANDLER

protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

4.4 如果此類是控制器或者有requestMapping註解,才會處理。

4.5 遍歷當前類的所有方法,查找包含RequestMapping註解的方法,然後保存到

AbstractHandlerMethodMapping.MappingRegistry註冊表中。

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());
 
		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

判斷當前方法是否包含requestMapping註解

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);
	}

最終會調用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod保存到URL和RequestMappinfo的映射註冊表中。

AbstractHandlerMethodMapping	
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}

具體的保存邏輯

在這個方法中主要操作的數據對象有四個,分別是mappingLookup、urlLookup、corsLookup和registry。下面對這四個對象進行說明:

  1. mappingLookup對象是Map結構,key表示mapping對象,value表示處理對象,在本例中key是RequestMappingInfo對象,value是Controller中的某一個方法。
  2. urlLookup對象是Map結構,key表示url,value表示mapping對象,本例中key是具體的url值”/demo/postMapping/”,value是RequestMappingInfo對象,
  3. corsLookup對象是Map結構,key表示處理方法(Controller中的某個方法),value表示跨域配置,本例中沒有進行跨域註解的使用因此數據不存在,如果需要看到跨域數據,可以在method上添加@CrossOrigin註解
  4. registry對象是Map結構,key表示mapping對象,value表示MappingRegistration對象
AbstractHandlerMethodMapping.MappingRegistry  內部類
public void register(T mapping, Object handler, Method method) {
 
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod);
 
				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}
 
				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}
 
			
 
				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

生成的數據如下:

5.系統攔截器列表初始化過程,會調用到AbstractHandlerMapping.initApplicationContext,這個會查找當前容器工廠中所有繼承瞭MappedInterceptor類的攔截器實例BEAN.然後保存到AbstractHandlerMapping.interceptors

protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}

二、調用HTTP請求根據PATH尋找接口方法流程

1.首先tomcat會調用DispatcherServlet.doDispatch方法,進行請求分發處理。

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) {
					noHandlerFound(processedRequest, response);
					return;
				}
 
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
				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;
			}
		
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
	
		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);
				}
			}
		}
	}

2.首先調用getHandler去根據請求PATH查找HandlerExecutionChain,HandlerExecutionChain就是一個RequestHandleMappinfo加上一個攔截器列表。會調用到AbstractHandlerMapping.getHandler

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 = obtainApplicationContext().getBean(handlerName);
		}
 
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
 
		return executionChain;
	}

3.最終會調用到AbstractHandlerMapping.lookupHandlerMethod根據PATH查找HandlerMethod,這裡面的 this.mappingRegistry.getMappingsByUrl就是初始化時的URL和RequestMappingInfo映射表。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}
 
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

調用堆棧1

調用堆棧2

4.

初始化HandlerExecutionChain攔截器列表,這個會查找當前容器工廠中所有實現瞭handleInteropr 的類,

AbstractHandlerMapping類	
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
 
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
		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;
	}

這個類會根據攔截器的URL匹配規則相應添加攔截器列表。

<mvc:interceptors>
 <bean class="com.tpw.component.HandlerInterceptor1"></bean>
    <mvc:interceptor>
        <mvc:mapping path="/user"/>
        <bean class="com.tpw.component.HandlerInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

5.調用所有攔截器的applyPreHandle方法

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

7.然後調用invocableMethod.invokeAndHandle(webRequest, mavContainer),首先通過反射調用handlerMethod中的bean的接口方法

ServletInvocableHandlerMethod
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) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}
 
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

9.最後調用HandlerMethodReturnValueHandlerComposite.handleReturnValue 進行返回值處理,例如將BEAN轉JSON,轉XML等。

9.1 這個找HANDLER的過程也是,根據此HANDLER是否支持此方法,如在方法上加上瞭@ResponseBody,則會由RequestResponseBodyMethodProcessor處理。

RequestResponseBodyMethodProcessor
public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
 
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

RequestResponseBodyMethodProcessor。
此類繼承瞭AbstractMessageConverterMethodProcessor,這個類會調用當前容器工廠中所有

9.2 由於我們在方法上加瞭@ResponseBody註解,所有此handler為

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
 
		Object body;
		Class<?> valueType;
		Type targetType;
 
		if (value instanceof CharSequence) {
			body = value.toString();
			valueType = String.class;
			targetType = String.class;
		}
		else {
			body = value;
			valueType = getReturnValueType(body, returnType);
			targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
		}
	
		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
						(GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {
					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
					if (body != null) {
						Object theBody = body;
						LogFormatUtils.traceDebug(logger, traceOn ->
								"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
						addContentDispositionHeader(inputMessage, outputMessage);
						if (genericConverter != null) {
							genericConverter.write(body, targetType, selectedMediaType, outputMessage);
						}
						else {
							((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
						}
					}
					else {
						if (logger.isDebugEnabled()) {
							logger.debug("Nothing to write: null body");
						}
					}
					return;
				}
			}
		}
 
	}

9.3 messageConverts的數據初始化來源為 RequestMappingHandlerAdapter.getDefaultArgumentResolvers方法中,

resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));

messageConverts來源於RequestMappingHandlerAdapter.messageConverters中。

在初始化時會,依賴註入當前系統中所有的messageConvert.

10.最後調用攔截器的所有postHandle方法進行,處理完回調。

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {
 
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

11.在渲染完輸出視圖後,會調用所有攔截器的afterCompletion方法,註意,JSON,XML這種沒有視圖,隻有HTML等才有。

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
			throws Exception {
 
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}

到此這篇關於springmvc path請求映射到bean 方法的流程的文章就介紹到這瞭,更多相關springmvc 請求映射內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: