溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

springmvc path請求怎么映射到bean方法

發(fā)布時間:2021-07-23 16:58:03 來源:億速云 閱讀:152 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“springmvc path請求怎么映射到bean方法”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、加載注冊流程

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

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

      3.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping為RequestMapping注解映射到后臺接口的注冊表。此類實現(xiàn)了InitializingBean接口,會觸發(fā)到

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方法中會先調(diào)用getCandidateBeanNames獲取當(dāng)前容器工廠的所有BEAN,然后逐個BEAN進(jìn)行處理。

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 判斷當(dāng)前BEAN是否HANDLER

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

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

springmvc path請求怎么映射到bean方法

 4.5 遍歷當(dāng)前類的所有方法,查找包含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);
			});
		}
	}

判斷當(dāng)前方法是否包含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);
	}

最終會調(diào)用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);
	}

具體的保存邏輯

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

  1. mappingLookup對象是Map結(jié)構(gòu),key表示mapping對象,value表示處理對象,在本例中key是RequestMappingInfo對象,value是Controller中的某一個方法。

  2. urlLookup對象是Map結(jié)構(gòu),key表示url,value表示mapping對象,本例中key是具體的url值"/demo/postMapping/",value是RequestMappingInfo對象,

  3. corsLookup對象是Map結(jié)構(gòu),key表示處理方法(Controller中的某個方法),value表示跨域配置,本例中沒有進(jìn)行跨域注解的使用因此數(shù)據(jù)不存在,如果需要看到跨域數(shù)據(jù),可以在method上添加@CrossOrigin注解

  4. registry對象是Map結(jié)構(gòu),key表示mapping對象,value表示MappingRegistration對象

AbstractHandlerMethodMapping.MappingRegistry  內(nèi)部類
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();
			}
		}

生成的數(shù)據(jù)如下:

springmvc path請求怎么映射到bean方法

5.系統(tǒng)攔截器列表初始化過程,會調(diào)用到AbstractHandlerMapping.initApplicationContext,這個會查找當(dāng)前容器工廠中所有繼承了MappedInterceptor類的攔截器實例BEAN.然后保存到AbstractHandlerMapping.interceptors

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

springmvc path請求怎么映射到bean方法

二、調(diào)用HTTP請求根據(jù)PATH尋找接口方法流程

  1.首先tomcat會調(diào)用DispatcherServlet.doDispatch方法,進(jìn)行請求分發(fā)處理。

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.首先調(diào)用getHandler去根據(jù)請求PATH查找HandlerExecutionChain,HandlerExecutionChain就是一個RequestHandleMappinfo加上一個攔截器列表。會調(diào)用到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.最終會調(diào)用到AbstractHandlerMapping.lookupHandlerMethod根據(jù)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);
		}
	}

調(diào)用堆棧1

springmvc path請求怎么映射到bean方法

調(diào)用堆棧2

springmvc path請求怎么映射到bean方法 4.

初始化HandlerExecutionChain攔截器列表,這個會查找當(dāng)前容器工廠中所有實現(xiàn)了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;
	}

springmvc path請求怎么映射到bean方法

這個類會根據(jù)攔截器的URL匹配規(guī)則相應(yīng)添加攔截器列表。

<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.調(diào)用所有攔截器的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.然后調(diào)用invocableMethod.invokeAndHandle(webRequest, mavContainer),首先通過反射調(diào)用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;
		}
	}

springmvc path請求怎么映射到bean方法

9.最后調(diào)用HandlerMethodReturnValueHandlerComposite.handleReturnValue 進(jìn)行返回值處理,例如將BEAN轉(zhuǎn)JSON,轉(zhuǎn)XML等。

 9.1 這個找HANDLER的過程也是,根據(jù)此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);
	}

springmvc path請求怎么映射到bean方法

RequestResponseBodyMethodProcessor。
此類繼承了AbstractMessageConverterMethodProcessor,這個類會調(diào)用當(dāng)前容器工廠中所有

springmvc path請求怎么映射到bean方法

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

springmvc path請求怎么映射到bean方法

9.3 messageConverts的數(shù)據(jù)初始化來源為 RequestMappingHandlerAdapter.getDefaultArgumentResolvers方法中,

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

messageConverts來源于RequestMappingHandlerAdapter.messageConverters中。

在初始化時會,依賴注入當(dāng)前系統(tǒng)中所有的messageConvert.

springmvc path請求怎么映射到bean方法

10.最后調(diào)用攔截器的所有postHandle方法進(jìn)行,處理完回調(diào)。

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.在渲染完輸出視圖后,會調(diào)用所有攔截器的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方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI