SpringMVC的执行流程

2023-05-16

前言

当你知道springMVC的执行流程的时候,会达到是事半功倍的学习效果。

SpringMVC执行流程

  • 首先明确:SpringMVC的执行过程就是:客户端或者浏览器发送请求到后端服务器,后端服务器会找到对应的方法执行相应的方法,这之间的复杂的过程就是SpringMVC执行流程的核心。
    注意:这里的方法就是控制层(controller类中的的方法,同时这个方法在底层就是handler中的handlerMethod)。hander就是SpringMVC执行流程的主角之一。

  • 客户端发送的请求到后端服务器,后端服务器首先会做什么呢?
    我们都知道springMVC框架是为了搭建javaWeb项目使用的框架工具。所以说后端服务器接收到请求之后就和javaweb执行流程一样。如下图:
    在这里插入图片描述

  • 那有一个问题:SpringMVC框架体现在哪呢????
    其实SpringMVC框架就体现在对以前的Servlet类进行包装,包装之后就成了SpringMVC核心代码了,这个代码就是DispatcherServlet类了

  • 现在我们重点放在DispatcherServlet类中,因为SpringMVC的执行流程就在其中。
    说到DispatcherServlet你应该要想到JavaWeb中的servlet类,并且知道Servlet类中对主要的方法:service()。
    我们先来看DispatcherServlet的继承结构图。
    在这里插入图片描述
    主要看蓝色箭头:
    在这里插入图片描述
    Servlet(接口)→HttpServlet → HttpServletBean→FrameworkServlet→DispatcherServlet
    在这里插入图片描述
    假如前端发送了一个GET请求,在HttpServlet 中会判断请求方式,而HttpServletBean、FrameworkServlet这两个类做的是一些初始化工作,最终还是执行DispatcherServlet类中的方法。

  • 我们都知道所有servlet中都有着一个最主要的方法:service()。在DispatcherServlet最主要的方法就是doservice()方法。
    在这个方法中所干的事情先做个总结在看源代码,主要是做两件事:
    1、设置参数,比如:设置应用上下文、字符编码、样式等等
    2、调用处理请求的核心方法doDispatch(request, response);

    //获取请求,设置一些request的参数,然后分发给doDispatch
	@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("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}
 
		// Make framework objects available to handlers and view objects.
		/* 设置web应用上下文**/
		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));
		}
		//Flash attributes 在对请求的重定向生效之前被临时存储(通常是在session)中,并且在重定向之后被立即移除
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		//FlashMap 被用来管理 flash attributes 而 FlashMapManager 则被用来存储,获取和管理 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);
				}
			}
		}
	}

  • 接着我们再看处理请求的核心方法doDispatch(request, response)
    通过request, response这两个参数,我们拿到了请求信息。这里主要做的事情有:
    1、通过请求信息判断是否有文件上传
    2、调用 getHandler(processedRequest);
    这个方法是利用请求信息得到请求的地址,例如请求地址为:http://localhost:8080/target。
    有了请求地址后再得到映射路径:/target 。(注意这个/target是通过请求路径得到的。)
    说明:后端我们写的所有的controller方法的映射路径和方法都是以key-value的形式存储在HandlerMapping集合当中。例如:下图,key为/target,value为这个方法tar()
    在这里插入图片描述
    通过映射路径/target去遍历HandlerMapping,如果HandlerMapping中的key有/servlet,就返回对应的方法。如果没有就会返回404错误。
    3、此时我们得到了方法tar(),接下来我们就是要去执行这个方法。
    如何执行?
    答:通过适配器执行,得先获得适配器:HandlerAdapter。
    在源代码中执行这个方法tar()前后,是由拦截器的,作用就是进一步去看你这个方法合不合理。
    合理的话就继续执行。过程如下:
    拦截器前置方法执行:preHandler方法
    执行方法,返回了ModelAndView
    拦截器后置方法1执行:PostHandle方法
    渲染视图
    拦截器后置方法2执行:AfterCompletion方法。
    源代码:
     /**
	 *将Handler进行分发,handler会被handlerMapping有序的获得
	 *通过查询servlet安装的HandlerAdapters来获得HandlerAdapters来查找第一个支持handler的类
	 *所有的HTTP的方法都会被这个方法掌控。取决于HandlerAdapters 或者handlers 他们自己去决定哪些方法是可用
	 *@param request current HTTP request
	 *@param response current HTTP response
	 */
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		/* 当前HTTP请求**/
		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);
 
				// getHandler中遍历HandlerMapping,获取对应的HandlerExecutionChain
                // HandlerExecutionChain,包含HandlerIntercrptor和HandlerMethod
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
 
				
				//获得HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
				//获得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;
					}
				}
				//如果有拦截器的话,会执行拦截器的preHandler方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
 
				//返回ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//当view为空时,,根据request设置默认view
				applyDefaultViewName(processedRequest, mv);
				//执行拦截器的postHandle
				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);
				}
			}
		}
	}

到这里SpringMVC的执行流程就好像结束了。剩下的就是交给前端将渲染出来的视图展现给客户。

我这里的 视图渲染 没说太清楚。sorry

大总结:(面试的话可以按照下面的来说)
在这里插入图片描述
注意注意:(重要的事情说三遍)
如果有哪里不对的地方,欢迎在评论区告诉我!!!!!!!!!!!!!!!!!
如果有哪里不对的地方,欢迎在评论区告诉我!!!!!!!!!!!!!!!!!
如果有哪里不对的地方,欢迎在评论区告诉我!!!!!!!!!!!!!!!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringMVC的执行流程 的相关文章

随机推荐