20210121 SpringMVC(一)

springmvc 同时被 2 个专栏收录
1 篇文章 0 订阅
1 篇文章 0 订阅

一、DispatcherServlet继承体系

在这里插入图片描述



FrameworkServlet:收口servlet doGet、doPost方法,本质调用doService,模板类,子类DispatcherServlet实现doService

HttpServletBean:引入Environment,继承HttpServlet,但是由子抽象类FrameworkServlet重写get,post方法。

1、FrameworkServlet方法定义

image.png

可以看到不管是do什么方法,都会调用processRequest(),也就是对各种类型请求统一收口了。

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

		// 将请求响应封装到ServletRequestAttributes
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

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

		// 将本地配置上下文和ServletRequestAttributes设置到线程上下文,后续任意节点可以从当前线程中获取
		initContextHolders(request, localeContext, requestAttributes);

		try {
			// 子类模板方法
			doService(request, response);
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		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");
					}
				}
			}
			// 发布请求处理完成事件 包括:请求url、请求方式、请求耗时、状态 等等
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

2)方法总结


1、监听ServletRequestHandledEvent可以获取所有请求的时间;例如 监控分析、日志、等


2、通过LocaleContextHolder可以获取当前请求的本地环境信息


3、通过RequestContextHolder可以获取请求链路的所有信息

2、DispatcherServlet定义

1)初始化过程



HttpServletBean init方法被调用时,触发子类FrameworkServlet 的 initServletBean()
![image.png](https://img-blog.csdnimg.cn/img_convert/5ff72686ab3200e47fdf600b36d5c6ff.png#align=left&display=inline&height=365&margin=[object Object]&name=image.png&originHeight=365&originWidth=1188&size=45906&status=done&style=none&width=1188)
initFrameworkServlet为空实现
关键是initWebApplicationContext();
内部onRefresh(wac);会触发DispatcherServlet 9大组件加载 ```java protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null;
	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent -> set
					// the root application context (if any; may be null) as the parent
					cwac.setParent(rootContext);
				}
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {
		// No context instance was injected at construction time -> see if one
		// has been registered in the servlet context. If one exists, it is assumed
		// that the parent context (if any) has already been set and that the
		// user has performed any initialization such as setting the context id
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// No context instance is defined for this servlet -> create a local one
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		// Either the context is not a ConfigurableApplicationContext with refresh
		// support or the context injected at construction time had already been
		// refreshed -> trigger initial onRefresh manually here.
		onRefresh(wac);
	}

	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
					"' as ServletContext attribute with name [" + attrName + "]");
		}
	}

	return wac;


<a name="PC1d2"></a>
### 2)九大组件初始化
```java
protected void initStrategies(ApplicationContext context) {
		// 处理上传,将request包装为MultipartHttpServletRequest
		initMultipartResolver(context);
		// 获取区域,local,国际化相关
		initLocaleResolver(context);
		// 解析主题
		initThemeResolver(context);
		// 根据请求匹配handlerMethod和符合请求的拦截器
		initHandlerMappings(context);
		// 根据handlerMethod找到适配器,并执行handlerMethod
		initHandlerAdapters(context);
		// 处理异常设置modelAndView
		initHandlerExceptionResolvers(context);
		// 从请求获取viewName
		initRequestToViewNameTranslator(context);
		// viewResolver根据viewName获取view
		initViewResolvers(context);
		// 重定向传递参数,重定向后从FlashMapManager的retrieveAndUpdate获取FlashMap设置到request备用
		initFlashMapManager(context);
	}

3、处理请求


之前说过FrameworkServlet会统一收口请求方式,然后调用子类doService。
dosService主要是封装request,初始设置一些对象local、主题。如果是重定向将数据设置到当前请求


还有一个细节快照存储
因为request在处理完业务流程后可能还需要使用,而且只缓存org.springframework.web.servlet开头的key
然后调用doDispatch,在finally还原快照

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		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(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// 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.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

1) 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 {
			// 封装model和view的容器
			ModelAndView mv = null;
			// 请求过程异常
			Exception dispatchException = null;

			try {
			    // 检查是否上传请求 并且根据初始化的上传组件包装为MultipartHttpServletRequest
				// POST请求 && ContentType开头 multipart/
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 根据request找handler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 根据handler找HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				// 处理get、head请求last-modified
				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;
					}
				}
				// 执行相应拦截器的preHandler
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// HandlerAdapter使用handler处理请求
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				// 如果异步处理直接返回
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				// 当view为空时(比如:handler返回值void),根据request设置默认view
				applyDefaultViewName(processedRequest, mv);
				// 执行相应拦截器的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) {
			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) {
					// 调用异步拦截器afterCompletion
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				// 删除上传请求的资源
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}





  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值