SpringMVC3-处理-源码跟踪

之前分析了,SpringMVC启动-源码跟踪,这里分析下SpringMVC是如何处理请求的。我们知道Servlet的处理请求的入口方法是:

public void service(ServletRequest req, ServletResponse res);

以一个Http get请求为例,绘制如下流程图:
这里写图片描述

  • 首先请求的入口是HttpServlet#service(ServletRequest req, ServletResponse res);
  • 然后HttpServlet,将请求参数转换为HttpServletRequest ,HttpServletResponse ,然后调用service(HttpServletRequest req, HttpServletResponse resp),并通过请求类型转发给不同类型的处理方法。
//javax.servlet.http.HttpServlet
 public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
		    //省略....
             doGet(req, resp);
         } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } //省略其他类型请求......    
        else {
            //异常处理
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
  • 接上面根据不同的请求类型,交由各类型对应的方法,由于FrameworkServlet重写了doGet()等方法,所以交由FrameworkServletdoGet()处理,然后由交由processRequest()继续后续处理。
	//org.springframework.web.servlet.FrameworkServlet
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		processRequest(request, response);
	}
  • FrameworkServletprocessRequest()处理后,交由doService()继续处理。
	protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;
  • 上述doService()方法是个abstract方法,所以这里由DispatcherServlet处理,处理完毕后交由doDispatch()进行结果返回。

请求的大致流程已经分析完毕,其中HttpServlet的处理比较简单,下面主要分析下FrameworkServletDispatcherServlet


FrameworkServlet

FrameworkServlet 重写了doGet,doPost,doPut,doDelete,doTrace等方法。并且将这些方法全部上交给processRequest()处理。

processRequest方法
processRequest是一个final方法,这说明它不允许被重写。

//org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

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

		//获取当前请求(线程)的LocaleContext
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		//重新设置当前请求的LocaleContext
		LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);

		//获取当前请求的RequestAttributes 
		RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
		
		ServletRequestAttributes requestAttributes = null;
		if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
			//将RequestAttributes转换为ServletRequestAttributes
			requestAttributes = new ServletRequestAttributes(request);
			
			//重新设置当前请求的requestAttributes
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}


		//try-catch省略...
		
		//实际处理入口
		doService(request, response);

		finally {
			// 恢复原来的previousLocaleContext
			LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
			if (requestAttributes != null) {
				//恢复原来的previousRequestAttributes
				RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
				//清空requestAttributes
				requestAttributes.requestCompleted();
			}
			
			//省略....
			if (this.publishEvents) {
				//无论成功与否,发布ServletRequestHandledEvent消息。
				long processingTime = System.currentTimeMillis() - startTime;
				this.webApplicationContext.publishEvent(
						new ServletRequestHandledEvent(this,
								request.getRequestURI(), request.getRemoteAddr(),
								request.getMethod(), getServletConfig().getServletName(),
								WebUtils.getSessionId(request), getUsernameForRequest(request),
								processingTime, failureCause));
			}
		}
	}

requestAttributes.requestCompleted()

//org.springframework.web.context.request.AbstractRequestAttributes 

/**
 * 标志请求结束
 * <p>Executes all request destruction callbacks and updates the
 * session attributes that have been accessed during request processing.
 */
public void requestCompleted() {
	//执行request拆除回调函数,并清空这些回调信息。
	executeRequestDestructionCallbacks();
	//更新session中所有受影响的属性
	updateAccessedSessionAttributes();
	//设置request无效
	this.requestActive = false;
}


private void executeRequestDestructionCallbacks() {
	synchronized (this.requestDestructionCallbacks) {
		for (Runnable runnable : this.requestDestructionCallbacks.values()) {
			runnable.run();
		}
		this.requestDestructionCallbacks.clear();
	}
}

ServletRequestHandledEvent
无论成功与否,最后都会发布ServletRequestHandledEvent事件,可以自定义ApplicationListener进行监听。注意需要将该Listener注册到spring容器中

public class ServletRequestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {
	
	@Override
	public void onApplicationEvent(ServletRequestHandledEvent event) {
		System.out.println(event.getDescription());
	}
}

DispatcherServlet

DispatcherServlet是SpringMVC 最核心的类,整个处理过程的顶层设计都在这里。经过上面的分析,可以得知DispatcherServlet 方法的入口是:doService()

doservice()

//org.springframework.web.servlet.DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

		//如果是<jsp:include>include请求,快照记录当前的attributes,并在处理完成之后进行还原。
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			logger.debug("Taking snapshot of request attributes before include");
			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));
				}
			}
		}

		//设置flashMap相关属性,主要用于Redirect转发时参数的传递。
		this.flashMapManager.requestStarted(request);

		// 对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());

		try {
			doDispatch(request, response);
		}
		finally {
			//"output" FlashMap保存在底层存储,并开始进行倒计时。默认180s.
			this.flashMapManager.requestCompleted(request);
			
			// 当时include请求时,恢复之前备份的属性
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}

FlashMap的作用:是在redirect请求时传递参数,
outputFlashMap:用于保存本次请求需要转发的属性。
inputFlashMap:用于保存上次请求转发过来的属性。
它的使用方式如下:

@RequestMapping("/submit")
	public String submit(RedirectAttributes attr) {
		//获取FlashMap方法1
		((FlashMap) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest().getAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE)).put("name", "特朗普传");
		
		//获取FlashMap方法2:RedirectAttributes.addFlashAttribute::参数放到outputFlashMap
		attr.addFlashAttribute("orderId", "9901");
		
		//获取FlashMap方法3:RedirectAttributes.addAttribute ::参数会拼接到url后面。
		attr.addAttribute("price", "zh-cn");
		return "redirect:detail";
	}
	
	@RequestMapping("/detail")
	public String detail(Model model) {
		System.out.println(model.asMap());
		return "success";
	}

doService主要对request设置了一些属性之后,最终将请求转发给doDispatch()方法。


doDispatch()结构
doDispatch的方法也非常简洁,从顶层设计了整个请求的处理过程。doDispatch的最核心代码只有4句,它们的任务分别是:

  1. 根据request找到Handler以及对应的interceptors
  2. 根据Handler找到对应的HandlerAdapter
  3. 用HandlerAdapter处理Handler
  4. 处理上面处理之后的结果(包含找到View并渲染给用户),最终调用afterCompletion

对应代码如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//确定当前请求的Handler
	mappedHandler = getHandler(processedRequest, false);
	
	//确定当前Handler的HandlerAdapter .
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

	//HandlerAdapter 执行Handler
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	
	//触发整个请求处理完毕回调方法afterCompletion  
	triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}

HandlerMapping :是用来寻找Handler的,可以理解为请求
Handler:也就是处理器,可以理解为Controller层。可以是类,也可是是方法。
HandlerAdapter:适配器。Handler处理请求可以是任意形式的,但是Servlet处理方法却是固定形式的,都是以request,response为参数的方法,怎么让Servlet调用灵活的Hander来进行处理呢,HandlerAdapter就是干这个事的。

引用一张图
这里写图片描述

doDispatch()详解

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		int interceptorIndex = -1;

		try {
			ModelAndView mv;
			boolean errorView = false;

			try {
				//检查是不是上传请求,如果是转换为MultipartHttpServletRequest
				processedRequest = checkMultipart(request);

				//根据request找到Handler
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				//根据Handler找到HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 处理GET,HEAD请求的last-modified header
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				//执行Handler注册的interceptors中的preHandle方法
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if (interceptors != null) {
					for (int i = 0; i < interceptors.length; i++) {
						HandlerInterceptor interceptor = interceptors[i];
						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
							return;
						}
						interceptorIndex = i;
					}
				}

				// HandlerAdapter使用Handler处理请求
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				//当View为空时(比如放回方法是Void时),根据request设置默认View
				if (mv != null && !mv.hasView()) {
					mv.setViewName(getDefaultViewName(request));
				}

				执行Handler注册的interceptors中的postHandle方法(逆序)
				if (interceptors != null) {
					for (int i = interceptors.length - 1; i >= 0; i--) {
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}
			}
			catch (ModelAndViewDefiningException ex) {
				logger.debug("ModelAndViewDefiningException encountered", ex);
				mv = ex.getModelAndView();
			}
			catch (Exception ex) {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				//处理异常请求。
				mv = processHandlerException(processedRequest, response, handler, ex);
				errorView = (mv != null);
			}

			//渲染视图。
			if (mv != null && !mv.wasCleared()) {
				render(mv, processedRequest, response);
				if (errorView) {
					WebUtils.clearErrorRequestAttributes(request);
				}
			}
			else {
				//省略log....
			}

			// 触发整个请求处理完毕回调方法afterCompletion  
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
		}//省略try-catch


		finally {
			//删除上传请求的资源
			if (processedRequest != request) {
				cleanupMultipart(processedRequest);
			}
		}
	}

拆分上述方法,
getHandler()
getHandler()找到请求的HandlerExecutionChain ,执行过程按照顺序执行preHandler方法,返回时逆序执行postHandler.

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	for (HandlerMapping hm : this.handlerMappings) {
		//省略log...
		
		//寻找到HandlerMapping匹配到的第一个HandlerExecutionChain。
		HandlerExecutionChain handler = hm.getHandler(request);
		if (handler != null) {
			return handler;
		}
	}
	return null;
}

GET,HEAD请求的Last-Modified
当浏览器第一次跟服务器请求资源(GET,HEAD请求)时,服务器返回的响应头中包含一个Last-Modified属性,代表本资源最后是什么时候修改的。在浏览器以后发送请求时会同时发送之前接收到Last-Modified,服务器接收到带有Last-Modified的请求时会用其值和自己实际资源修改时间作比较,如果过期了则返回新的资源(同时返回新的Last-Modified),否则返回304状态码表示资源未过期,浏览器直接使用之前缓存的结果。

triggerAfterCompletion()

private void triggerAfterCompletion(HandlerExecutionChain mappedHandler,
			int interceptorIndex,
			HttpServletRequest request,
			HttpServletResponse response,
			Exception ex) throws Exception {

		// 触发整个请求处理完毕,回调interceptor的afterCompletion方法 .
		if (mappedHandler != null) {
			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
			if (interceptors != null) {
				for (int i = interceptorIndex; i >= 0; i--) {
					HandlerInterceptor interceptor = interceptors[i];
					try {
						interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
					}
					catch (Throwable ex2) {
						logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
					}
				}
			}
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值