关于springboot的Rest请求映射处理的源码分析(二)

前面我们知道了他怎么处理表单映射,这里我们来研究一下,他是如何处理具体请求的。也就是说我有那么多/user你是怎么定位到我在哪个cotroller,并且你是怎么定位到我具体是哪个接口。
这里我们就来逐步定位一下这个问题。

一、组件分析

老路子,我们的所有逻辑都是一个个组件串起来的,我们先把组件一个个拿出来。看看他的功能。

组件1、@RequestMapping

我们的请求接口都标注了这个注解,然后生效的,虽然没啥逻辑,但是还是列出来。

组件2、请求处理器RequestMappingHandlerMapping

我们看到他在WebMvcAutoConfiguration中注册了一堆bean,是用来做请求映射处理的,其中有一个名字就是RequestMappingHandlerMapping 一看就是用来处理我们的@RequestMapping注解请求的。其余还有什么WelcomePageHandlerMapping 是用来处理欢迎页的,也就是那个默认的index.html页面的跳转。我们不管这个,就看到他给了RequestMappingHandlerMapping。

@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
	// Must be @Primary for MvcUriComponentsBuilder to work
	return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
			resourceUrlProvider);
}

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
		FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
	WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
			new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
			this.mvcProperties.getStaticPathPattern());
	welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
	welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
	return welcomePageHandlerMapping;
}

貌似都有了,有了映射,有了映射处理器,此外的就是看逻辑了。

二、源码流程

1、找到入口

我们先明确一下,怎么分析mvc这类源码,我们都知道入口类就是DispatcherServlet。而他再怎么花哨也就是个servlet,既然你是servlet,那就肯定主要逻辑实现在那个叫做doGet和doPost的方法里面。
我们就去找这个方法,可能这个里面没有,但是父类里面绝壁有。我们最后在FrameworkServlet这个类里面找到了这两个方法。

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}

/**
 * Delegate POST requests to {@link #processRequest}.
 * @see #doService
 */
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}

	......还有什么doDelete之类的不说了。都一样

我们看到他其实是都走了processRequest(request, response);这个方法。最后我们一路翻他的主要实现逻辑依次是processRequest->org.springframework.web.servlet.DispatcherServlet#doService->
org.springframework.web.servlet.DispatcherServlet#doDispatch 这就是我们每次定位MVC问题的那个入口类。就是这么分析来的。
于是我们来看这个类的这个方法。

2、主要逻辑

org.springframework.web.servlet.DispatcherServlet#doDispatch
一些没用的代码我就删了,我们这里主要关注请求映射处理。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {        
	HttpServletRequest processedRequest = request;                                                            
	// 删除没用的                                
                                                                                                              
	try {                                                                                                     
		ModelAndView mv = null;                                                                               
		Exception dispatchException = null;                                                                   
                                                                                                              
		try {  
			// 看是不是文件上传请求,我们这里不关注                                                                                               
			processedRequest = checkMultipart(request);                                                       
			multipartRequestParsed = (processedRequest != request);                                           
                                                                                                              
			// Determine handler for the current request.  
			// 看着源码注释也知道他是决定你哪个处理器来处理你的请求的,其实就是这里                                                   
			mappedHandler = getHandler(processedRequest);                                                     
			
			// 后续就是走具体逻辑了,我们只关注他怎么找到请求执行的。后面不关注                                 
		}                                                                                                     
		                                        
}                                                                                                             

所以我们看到主要逻辑落在了mappedHandler = getHandler(processedRequest);这个方法上面,
我们就来看看这个方法。
org.springframework.web.servlet.DispatcherServlet#getHandler

@Nullable                                                                                    
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {    
	if (this.handlerMappings != null) {                                                      
		for (HandlerMapping mapping : this.handlerMappings) {                                
			HandlerExecutionChain handler = mapping.getHandler(request);                     
			if (handler != null) {                                                           
				return handler;                                                              
			}                                                                                
		}                                                                                    
	}                                                                                        
	return null;                                                                             
}                                                                                            

我们看到他遍历了一个handlerMapping的集合,然后看哪个能处理你的这个请求,就直接返回了,我们这里需要debug一下,看看这个集合里面都有啥。
我们把断点打到这里。然后调用一个GET请求。
在这里插入图片描述
在这里插入图片描述
我们看到他其实就是我们组件2的集合,其中第一个就是我们的组件2,而且他里面加载了所有的标注了@ReauestMapping的请求路径的集合,他在初始化加载的时候就封装进去了。于是我们知道他在组件2里面就有了我们所有的请求路径的类,接口,以及其他信息,都封装了。
封装结构为map,其中key就是请求路径,比如/user
value就是你这个路径哪个controller,哪个方法之类的。这样就能一下就找到了。
我们来直接进入这个方法。看看他怎么判断的。我们debug进去。
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 这里就是处理逻辑,我们再进去。
	Object handler = getHandlerInternal(request);
	// 省略没用的......
	return executionChain;
}

最后来到这里。org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	/**
		lookupPath:就是我们的这次请求的路径/user
		而我们一共有四个/user。分别是post get put delete所以这里directPathMatches 
		会拿回来四个结果。
	*/
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		// 这里会根据请求类型,返回其中对的上的,比如我们这次请求的是GET,那就返回GET的那个
		// /user,把这个请求的处理信息放在了matches集合里面
		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()) {
		// 取出找到的那一个,这样我们就找到了我们的controller和接口
		Match bestMatch = matches.get(0);
		// 如果找到了多个,哥们你url写重复了,抛出异常Ambiguous...之类的
		if (matches.size() > 1) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		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);
	}
}

这样我们就找到了我们的controller和接口,后面就是请求执行了,之前我们看过了,其实是反射。

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值