文档章节

Spring framework MVC源码阅读笔记(一)

第五郎
 第五郎
发布于 2016/09/14 16:45
字数 1171
阅读 28
收藏 2

org.springframework.web.servlet.DispatcherServlet

下图来自Spring官方文档,MVC对请求的处理流程

    上图中Front controller由DispatcherServlet充当,负责拦截和响应请求。实际请求的处理和响应的生成由Controller完成,这里的Controller具体就是平时用@Controller注解配置的类,这些类中由@RequestMapping配置的方法对应Handler,Spring MVC提供了HandlerMapping接口用于保存请求与Handler之间的对应的关系,请求最终会交给最匹配的Handler来处理。(View template部分暂缺,还说不清)

下图为DispatcherServlet继承结构

该图从Spring Framewoek API文档截得,从中可以看到DispatcherServlet继承结构

该类根据配置拦截请求,从图片和类名可知该类就是是一个Servlet,那么普通的Servlet所用有的特性该类自然也有。

下图为Servlet配置信息,该信息来自Servlet规范(该文档非常值得一看)

先看DispatcherServlet的一般配置

<web-app>

    <servlet>
        <servlet-name>example</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>example</servlet-name>
        <url-pattern>/example/*</url-pattern>
    </servlet-mapping>

</web-app>

    该类由容器负责实例化,load-on-startup为1应该是在容器启动是就加载该类。Servlet配置信息比较关注init-param节点,该节点可以为Servlet提供一些参数,通过复写init方法可以获得,DispatcherServlet的属性也是通过这个方式配置的,其具体实现在祖先类HttpServletBean中找到,不过之前平时使用时从来没配过。

    自定义的Servlet通常会继承javax.servlet.http.HttpServlet然后根据需要复写不同的方法,DispatcherServlet只复写了doService方法,其他的都交由祖先类处理。

    以下是doService的具体实现

    @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.
        //总而言之先保存下request携带的属性,从代码可以看出只保留了MVC相关的属性
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			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));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
        //为request添加一些属性,在真正负责处理请求的代码使用(?)
		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());

        //Flash相关,暂时不管
		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);
				}
			}
		}
	}

DispatcherServlet中维护了一个(或不止一个Map)用于保存处理者和请求之间的对于关系,从中获取最匹配的Handler来处理请求,如果找不到大概就会返回404的错误。以下为挑选Handler及实际处理请求实现:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
        //看字面意思,为处理异步请求做准备,到目前为止没怎么用过异步Servlet
		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.
                //选择具体的Handler
				mappedHandler = getHandler(processedRequest);
				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.
                //关于HTTP协议中缓存的部分,对于动态生成的内容使用缓存不熟
				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(processedRequest, mv);
				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);
			}
			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) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}
//逻辑不复杂,就是从Map中获取Handler,至于该Map是如何维护的还要再看
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

未完,待续……

© 著作权归作者所有

共有 人打赏支持
第五郎
粉丝 10
博文 53
码字总数 28957
作品 0
南京
高级程序员
私信 提问
springmvc学习笔记(1)-框架原理和入门配置

springmvc学习笔记(1)-框架原理和入门配置 标签: springmvc [TOC] 本文主要介绍springmvc的框架原理,并通过一个入门程序展示环境搭建,配置以及部署调试。 springmvc是spring框架的一个模块...

brianway
2016/03/08
271
0
Spring 官方文档第十六章笔记(1):SpringMVC简介

Spring Reference第十六章笔记(1)Introduction to Spring Web MVC framework Spring MVC的核心是DispatcherServlet。DispatcherServlet根据配置文件的配置将请求分发给各个 Controller 。 ...

LiJIaming
2012/05/31
0
0
Java基础教程:tutorialspoint-spring mvc

教程: 来自turorialspoint的Spring MVC 4.1.6教程(英文),官网:https://www.tutorialspoint.com/springmvc/index.htm 离线版本:(链接: https://pan.baidu.com/s/1hsvL7wS 密码: vg7x)......

easonjim
2017/08/28
0
0
springmvc源码解析合集

更多精彩源码解析文章请关注”天河聊架构“微信公众号。 springmvc源码解析之组件介绍 springmvc源码解析之配置加载SpringServletContainerInitializer springmvc源码解析之配置加载Context...

天河2018
03/27
0
0
Spring MVC 原理探秘 - 一个请求的旅行过程

1.简介 在前面的文章中,我较为详细的分析了 Spring IOC 和 AOP 部分的源码,并写成了文章。为了让我的 Spring 源码分析系列文章更为丰富一些,所以从本篇文章开始,我将来向大家介绍一下 Sp...

coolblog.xyz
2018/07/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

总结:volatile关键字

实现内存可见性原理: 对volatile变量执行写操作时,会在写操作之后加入一条store指令,将CPU缓存数据强制刷新到主内存中 对volatile变量执行读操作的时候,会在读操作前加入一条load指令,重...

浮躁的码农
21分钟前
0
0
OSChina 周六乱弹 —— 看见这花臂了么?赶紧叫大佬!

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享周华健的单曲《有没有一首歌会让你想起我》 《有没有一首歌会让你想起我》- 周华健 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
98
4
Confluence 6 升级中的一些常见问题

升级的时候遇到了问题了吗? 如果你想尝试重新进行升级的话,你需要首先重新恢复老的备份。不要尝试再次对 Confluence 进行升级或者在升级失败后重新启动老的 Confluence。 在升级过程中的一...

honeymoose
今天
2
0
C++随笔(四)Nuget打包

首先把自己编译好的包全部准备到一个文件夹 像这样 接下来新建一个文本文档,后缀名叫.nuspec 填写内容 <?xml version="1.0"?><package xmlns="http://schemas.microsoft.com/packaging/201......

Pulsar-V
今天
3
0
再谈使用开源软件搭建数据分析平台

三年前,我写了这篇博客使用开源软件快速搭建数据分析平台, 当时收到了许多的反馈,有50个点赞和300+的收藏。到现在我还能收到一些关于dataplay2的问题。在过去的三年,开源社区和新技术的发...

naughty
今天
20
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部