Spring MVC请求执行全流程核心源码解析

Spring MVC框架基本是Java服务中不可缺少的功能,很多时候我们只知道如何使用,并不清楚如何解析请求的,如何实现Restful、如何返回结果甚至渲染页面的。本文就从源码讲解DispatcherServlet如何帮助我们解析客户端发起的请求。

本文结合SpringBoot讲解,先引用一张架构图,先大致知道每个组件的作用,后续再一一剖析。

架构图

1.DispatchServlet

处理请求的核心类

1.引入容器

我们先查看SpringBoot自动装配类,确定是否会在容器中引入,同时了解一下除了核心类外还有什么被引入到容器中,我们打开spring-boot-autoconfigure-2.3.7.RELEASE.jar包的spring.factories文件可以看到DispatcherServletAutoConfiguration配置类会被自动装配进我们容器中。

在这里插入图片描述

2.定位具体方法处

这个时候我们肯定还是不清楚,DispatcherServlet调用了哪个方法进行处理,我们可以搜索do开头的非私有方法,可以看到有两个 doServicedoDispatch,不知道哪个没关系,可以在两个方法打个断点,然后给当前应用发起个请求,看看最终会进入到哪个方法中。

在这里插入图片描述

根据debug结果,我们可以发现两个方法都会执行,先执行doService后执行doDispatch。

在这里插入图片描述

那么我们可以确定doDispatch就是具体请求解析和执行的位置。

3.doDispatch

1.为当前请求选择合适的处理器(即Controller)

HandlerExecutionChain mappedHandler = null;
// 省略...
mappedHandler = getHandler(processedRequest);

进入该方法,可以发现我们会有很多个处理器选择器(HandlerMapping),会根据具体的请求选择是否有匹配的handler。

	// 初始化时候会从容器中获取,获取不到会设置默认的handlermapping
	@Nullable
	private List<HandlerMapping> handlerMappings;

	/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				// 如果能选择到合适的handlermapping
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

接着我们进入到getHandler中看看是如何选择合适的handler的,我们会发现getHandler这个方法,只在抽象类做了统一实现,所以我们定位到AbstractHandlermapping::getHandler中

	/**
	 * Look up a handler for the given request, falling back to the default
	 * handler if no specific one is found.
	 * @param request current HTTP request
	 * @return the corresponding handler instance, or the default handler
	 * @see #getHandlerInternal
	 */
	@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// 核心方法,用于选择handler
		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);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			config = (config != null ? config.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

我们继续进入到核心方法getHandlerInternal(request)中。发现最后都会调用到AbstractHandlerMethodMapping::getHandlerInternal中

	/**
	 * 根据request寻找合适的handler
	 */
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		request.setAttribute(LOOKUP_PATH, lookupPath);
		this.mappingRegistry.acquireReadLock();
		try {
			// 核心方法用于获取handlermethod返回
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

我们进入核心方法中可以看到它调用了内部mappingRegistry来进行查找。而不同的handlermapping实现类,内部的MappingRegistry都是不同的对象。

在这里插入图片描述

所以可以从源码看出来,最终就是通过MappingRegistry来找到恰当的HandlerMethod(Controller)。 而最后它会封装成HandlerExecutionChain返回给DispatchServlet。 本质是把handler、interceptors一起封装进行返回。

2.MappingRegistry如何将Controller注册的?

上节说到,最终我们会根据MappingRegistry来进行handler方法的查找,那么这个mapping映射是怎么注册的呢?

我们观察MappingRegistry这个类,它是在AbstractHandlerMethodMapping中的内部类,我们观察有没有什么方法是往这个集合添加元素的。

我们看到内部有个方法register,比较像是注册的类,这个时候我们可以给个断点,运行后印证一下自己想法。

在这里插入图片描述

最终我们定位到AbstractHandlerMethodMapping的afterPropertiesSet方法。 最后恍然大悟,原来是创建bean的时候,回调了初始化方法来注册所有的handler。

3.拦截器调用

在这里插入图片描述

我们实现的拦截器就是在此处调用的,方法处理前,处理后,完成结果封装

4.具体Controller执行

前面说了具体执行方法封装成了HandlerMethod,但是因为HandlerMethod类型不统一,所以为了能够统一的执行,封装了一层HandleAdpater适配器,不同适配器处理不同类型的HandlerMethod

	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	// 不同的HandlerMethod选择对应的适配器来执行。
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
	
	}

最后关键执行方法。

	// Actually invoke the handler.
	 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	
		return handleInternal(request, response, (HandlerMethod) handler);
	}

进入到handleInternal中

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// 执行处理方法
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

进入到invokeHandlerMethod中,可以发现核心方法

invocableMethod.invokeAndHandle(webRequest, mavContainer);
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 {
			// 处理返回值,比如@ResponseBody就是在此处执行
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

4.总结

本文对Spring MVC执行请求过程做了核心方法的讲解,解释了其中主流程核心源码。Spring MVC有九大组件,不同组件负责处理入参、结果、视图、异常等等,各有所长,涵盖http请求所有类型。

参考:https://blog.csdn.net/qq_44891295/article/details/116809998

https://zhuanlan.zhihu.com/p/354131900

https://blog.csdn.net/weixin_38413579/article/details/97939757

end
  • 作者:Endwas(联系作者)
  • 发表时间:2022-12-07 23:41
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转博主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者名字和博客地址
  • 评论