Springboot处理请求流程源码分析

  • Post author:
  • Post category:其他


SpringMVC处理请求的核心类是:DispatcherServlet,所有的 Web 请求都需要通过它来处理,进行转发,匹配,数据处理后,并转由页面进行展现,它是 SpringMVC 最核心的部分。Tomcat会将所有的请求都交给 DispatcherServlet 的 doService 方法处理。本次分析主要围绕DispatcherServlet来进行的。本次分析需要查看源码,如何查看源码可以参考:

点击跳转

简易的请求处理时序图:

在这里插入图片描述

DispatcherServlet类的继承关系图

在这里插入图片描述

以下内容为节省篇幅,仅粘贴了部分代码

首选我们查看DispatcherServlet的doService方法,它将请求转交给了doDispatch方法。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		try {
			//将请求交由doDispatch处理
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

我们查看doDispatch方法的实现,他的一个简易的流程是:首先是找到处理当前请求的Controller,通过getHandler方法去匹配找到对应的Controller,在通过getHandlerAdapter方法找到它的适配器,由适配器来执行Controller接口的方法;做好准备就开始执行了,首先依次执行拦截器的preHandle方法,第二步执行Controller中的方法,第三步执行拦截器的postHandle,最后处理返回结果,执行拦截器的afterCompletion方法

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

				// 找到处理当前请求的Controller
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 找到当前的处理器的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				//按照list顺序执行拦截器的preHandle方法,返回false就返回了
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// 通过动态代理调用controller的方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//根据返回是否需要匹配视图
				applyDefaultViewName(processedRequest, mv);
				
				//按照list顺序执行拦截器的postHandle方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			//处理返回结果,在这将调用拦截器的afterCompletion方法
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			//发生异常执行拦截器的afterCompletion方法
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		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);
				}
			}
		}
	}

SpringMVC 是如何根据请求地址找到我们对应的Controller以及方法的呢?

Spring将请求与处理方法的对应关系缓存在了

MappingRegistry

这个对象中,这个对象有几个Map成员,缓存的内容即key为请求路径,value为处理方法。

	class MappingRegistry {
		//缓存请求映射与处理方法
		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
		//缓存请求路径与对应的方法类型(POST,GET这种)
		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
		
		}

以下是存储的请求路径与对应的Controller的debug信息

根据请求路径找的对应的处理方法的的关键在AbstractHandlerMethodMapping类的lookupHandlerMethod方法。在doDispatch方法中,调用本类中的getHandler方法,在调用AbstractHandlerMapping类中的getHandler方法找到对应的handler

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

AbstractHandlerMapping类中的getHandler方法的实现

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//根据请求匹配找到handler即Controller信息
		Object handler = getHandlerInternal(request);
	}

AbstractHandlerMethodMapping类中的getHandlerInternal方法的实现

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		//拿到请求路径
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		this.mappingRegistry.acquireReadLock();
		try {
			//根据请求路径找到controller 就是根据map匹配到controller
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

AbstractHandlerMethodMapping类中的lookupHandlerMethod的实现,根据请求地址匹配Controller

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		//根据请求地址找到对应的路径和请求方式
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			//找到对应的controller信息
			addMatchingMappings(directPathMatches, matches, request);
		}
	}

刚刚我们找到了我们请求对应的Controller,那Spring是如何调用我们的Controller对应的方法的呢?

在DispatcherServlet的doDispatch方法中,通过 ha.handle(processedRequest, response, mappedHandler.getHandler()) 这段代码执行我们的Controller的方法,跟进执行发现是由ServletInvocableHandlerMethod类的invokeAndHandle来执行的

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		//执行Controller中的方法
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		//设置响应码
		setResponseStatus(webRequest);

	}
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
		//组装Controller中方法需要的参数
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Arguments: " + Arrays.toString(args));
        }
		//通过反射调用Controller方法
        return this.doInvoke(args);
    }

以上是本次Springboot处理请求的源码分析记录,由于处理流程比较繁杂,仅挑选了部分代码讲解,如有问题欢迎指正



版权声明:本文为qq_33505611原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。