SpringMVC源码剖析(二)SpringMVC是如何处理请求的

本文详细解析了Spring MVC中请求的处理流程,从Servlet到HandlerMapping、HandlerAdapter再到View的全过程,介绍了关键组件的作用及其实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析思路:

肯定是3层Servlet如何处理以及每层具体做了什么,最后分析最重要的一个方法doDispatch

回顾一下Servlet的请求处理过程  在HttpServlet中的service方法根据不同的动作分发了不同7种的请求


HttpServletBean

作用主要参加了创建工作,并没有涉及到请求的处理   这一步它没有具体处理请求


FrameworkServlet

在该类中重写了service   doXXX(除了doHead外的所有方法)

并且在service方法中增加了PATCH类型的处理  其他请求默认给父类处理

而doGet  doPost  doDelete  doPut都是自己处理  所有自己需要处理的请求都交给了processRequest方法统一处理

protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (RequestMethod.PATCH.name().equalsIgnoreCase(request.getMethod())) {
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}
我们发现HttpServlet是按不同请求类型路由到不同的方法处理  而这里相反

它采用另外一种方式处理将不同类型的请求处理用不同的Handler处理  这里后面再讲



思考???为何还不在service中覆盖service


下来分析processRequest方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		//这里省略了异常的捕获
                try{
               doService(request, response);
		}
		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					}
					else {
						this.logger.debug("Successfully completed request");
					}
				}
			}

			publishRequestHandledEvent(request, startTime, failureCause);
		}
	}

我们发现它调用了doService模板方法,其他的处理逻辑日后再补充


我们总结一下在service中添加了对PATCH的处理,并将所有需要自己处理的请求集中到了processRequest方法进行统一处理

然后就是processRequest交给了doService处理  另外是对使用当前request获取到的LocaleContext和RequestAttributes进行了保存,以及处理完之后的恢复,在最后发布了ServletRequestHandlerEvent事件


我们这事来分析doService

	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //这里将日志省略

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
              //当include请求满足对request的Attribute做快照备份
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<String, Object>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }
              //对request限制一些属性
        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.//还原request的快照
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }
		

好像有点复杂  看了一脸懵逼

但是简单的比如它是调用doDispatch

而在调用doDispatch前做了一些事情

首先判断是不是include请求  如果是则对request的Attribute做个快照备份 等doDispatch之后(如果不是异步请求且未完成)进行还原

在做完之后对request设置一些属性


具体设置了什么呢

// Make framework objects available to handlers and view objects.
	request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
	request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
	request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
	request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
设置了4个属性webApplicationContext  ,localeResolver,themeResolver,themeSource在之后介绍的handler和view中需要使用到时候在分析

后3个属性和flashMap相关,主要用于Redirect转发时参数的传递  这里我没看懂

关于flashMap的使用我后面再更新  现在还不太会


整理一下 doService对request设置了一些属性  如果是include请求还要对request当前的属性做快照备份 并在处理结束后恢复  最后把请求给了doDispatch


再来看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, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				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 (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

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

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
				}
			}
		}
	}
是不是有点长啊 看着费力气

我们抓住重要的看  看核心语句

	mappedHandler = getHandler(processedRequest, false);
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

核心代码4句 做了四件事

1.根据request找到Handler

2.根据Handler找到相应的HandlerAdapter

3.用HandlerAdapter处理Handler

4.调用processDispatchResult方法处理上面处理之后的结果(包括找到View并渲染输出给用户)


关于HandlerMapping,Handler,HandlerAdapter区别

后面再细讲啊

通俗一点理解

Handler是用来干活的工具 HandlerMapping是根据干的活找相应的工具 HandlerAdapter是使用工具干活的人

在SpringMVC中的9大组件HandlerAdapter最复杂

除此之外viewResolver是根据 需要查找view的 view是展示数据的


现在就很好理解他们的逻辑了

根据HandlerMapping找到干活的Handler  找到使用Handler的HandlerAdapter  让个HandlerAdapter使用Handler干活  干完活后将结果提交上去(通过view展示给用户)


然后在回到doDispatch分析还是很复杂

doDispatch可以大体分两部分  处理请求渲染页面

先看它定义的变量

此处省略1万字 太难了  ....................分析不了  以后再说

我们可以看到它是在顶层设计好分配给不同的组件去实现的


这篇文章只是提供了处理请求的大概思路具体的后续再更新


欢迎关注我的个人订阅号,我会推送更好的文章给大家

订阅号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值