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

 更新时间:2021年07月23日 12:06:08   作者:乘风破浪的码农  
这篇文章主要介绍了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 请求映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot 表单验证篇

    Spring Boot 表单验证篇

    在表单提交的时候,我们需要进行验证。下面通过本篇文章给大家介绍Spring Boot 表单验证篇,需要的朋友可以参考下
    2017-08-08
  • java8如何根据某一属性条件快速筛选list中的集合

    java8如何根据某一属性条件快速筛选list中的集合

    这篇文章主要介绍了java8如何根据某一属性条件快速筛选list中的集合,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 解析Java中的static关键字

    解析Java中的static关键字

    static是方便在没有创建对象的情况下进行调用(方法/变量)。显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。static可以用来修饰类的成员方法、类的成员变量,另外也可以编写static代码块来优化程序性能
    2021-06-06
  • Java如何优雅替换if-else语句

    Java如何优雅替换if-else语句

    当逻辑分支非常多的时候,if-else套了一层又一层,那么如何干掉过多的if-else,本文就详细的介绍一下,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • 使用IDEA搭建MyBatis环境详细过程

    使用IDEA搭建MyBatis环境详细过程

    这篇文章主要介绍了使用IDEA搭建MyBatis环境的相关知识,包括创建项目的过程及导入mybatis的核心jar包的详细说明,本文通过图文实例代码相结合给大家介绍的非常详细,需要的朋友可以参考下
    2021-05-05
  • springboot如何查找配置文件路径的顺序和其优先级别

    springboot如何查找配置文件路径的顺序和其优先级别

    此文是在工作中遇到的关于springboot配置文件的问题,在网上查阅资料和自己测试之后记录的,以便日后查阅。希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 如何获取jar包resources文件路径

    如何获取jar包resources文件路径

    java项目发布到jar之后找不到文件路径,遇到这样问题如何处理呢,下面小编给大家分享获取jar包resources文件路径的解决方法,感兴趣的朋友一起看看吧
    2023-12-12
  • JAVA实现 SpringMVC方式的微信接入、实现简单的自动回复功能

    JAVA实现 SpringMVC方式的微信接入、实现简单的自动回复功能

    这篇文章主要介绍了JAVA实现 SpringMVC方式的微信接入、实现简单的自动回复功能的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • 详解Java如何获取文件编码格式

    详解Java如何获取文件编码格式

    这篇文章主要介绍了详解Java如何获取文件编码格式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-01-01
  • JNDI简介_动力节点Java学院整理

    JNDI简介_动力节点Java学院整理

    这篇文章主要介绍了JNDI简介,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08

最新评论