前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC源码解析DispatcherServlet#doDispatch方法流程

SpringMVC源码解析DispatcherServlet#doDispatch方法流程

作者头像
JavaEdge
发布2021-02-22 14:34:18
5540
发布2021-02-22 14:34:18
举报
文章被收录于专栏:JavaEdgeJavaEdge

DispatcherServlet是前端控制器设计模式的实现,提供了Spring Web MVC的集中访问点, 而且负责职责的分派,而且与Spring Ioc容器无缝集成, 从而可以获的Spring的所有好处。

作用

DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:

  • 文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析
  • 通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器)
  • 、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器)
  • 通过ViewResolver解析逻辑视图名到具体视图实现
  • 本地化解析
  • 渲染具体的视图等
  • 如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。

DispatcherServlet的工作流程

DospatcherServlet实际上是一个Servlet(它继承HttpServlet)。DispatcherServlet处理的请求必须在同一个web.xml文件里使用url-mapping定义映射。这是标准的J2EE servlet配置。

代码语言:javascript
复制
	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);
				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.
				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;
					}
				}
				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) {
				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()) {
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

下面我们分析一下这些代码的意思,先看这句代码:

代码语言:javascript
复制
processedRequest = checkMultipart(request);

检查这个请求是不是文件上传的请求的。我们具体的看一下它是怎么判断是否是文件上传的。

checkMultipart

转换请求到multipart请求,使multipart解析器可用。如果没有解析器被设置,只需使用现有的请求

代码语言:javascript
复制
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
	if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
		if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
		}
		else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
		}
		else {
			return this.multipartResolver.resolveMultipart(request);
		}
	}
	return request;
}

这里先是判断multipartResolver这个类是不是为空 multipartResolver是需要我们进行配置的,通常配置如下所示:

代码语言:javascript
复制
>

如果没有配置MultipartResolver的话,则认为不是文件上传的请求,如果配置了MultipartResolver的话,调用isMultipart方法验证是不是文件上传的请求,isMultipart方法的内容如下:

代码语言:javascript
复制
public boolean isMultipart(HttpServletRequest request) {
	return (request != null && ServletFileUpload.isMultipartContent(request));
}

在这里我们看到了一个类是:ServletFileUpload文件上传工具包:commons-fileupload中的类。 SpringMVC对文件上传的处理是借助于commons-fileupload包来实现的。我们去isMultipartContent这个方法中看一下:

代码语言:javascript
复制
public static final boolean isMultipartContent(
        HttpServletRequest request) {
    if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
        return false;
    }
    return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}

这里首先判断一下是不是POST请求,如果不是POST请求直接返回false,接着通过isMultipartContent方法来继续验证:

代码语言:javascript
复制
public static final boolean isMultipartContent(RequestContext ctx) {
    String contentType = ctx.getContentType();
    if (contentType == null) {
        return false;
    }
    if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
        return true;
    }
    return false;
}

如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求。

代码语言:javascript
复制
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
	if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
		if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
		
		}
		else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
			
		}
		else {
			return this.multipartResolver.resolveMultipart(request);
		}
	}
	
	return request;
}

如果是文件上传请求,则继续判断这个请求是不是已经被转换为MultipartHttpServletRequest类型了。 在Spring-Web这个jar中有一个过滤器org.springframework.web.multipart.support.MultipartFilter 如果在web.xml中配置这个过滤器的话,则会在过滤器中提前判断是不是文件上传的请求,并将请求转换为MultipartHttpServletRequest类型。 这个过滤器中默认使用的MultipartResolver为StandardServletMultipartResolver。 如果不是MultipartHttpServletRequest类型的话,则判断是不是出现异常了。如果上面这两步返回的都是false,则会执行这句:this.multipartResolver.resolveMultipart(request),将请求转换为MultipartHttpServletRequest类型。我们具体看一下:

代码语言:javascript
复制
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
	Assert.notNull(request, "Request must not be null");
	if (this.resolveLazily) {
		return new DefaultMultipartHttpServletRequest(request) {
			@Override
			protected void initializeMultipart() {
				MultipartParsingResult parsingResult = parseRequest(request);
				setMultipartFiles(parsingResult.getMultipartFiles());
				setMultipartParameters(parsingResult.getMultipartParameters());
				setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
			}
		};
	}
	else {
		MultipartParsingResult parsingResult = parseRequest(request);
		return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
				parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
	}
}

在CommonsMultipartResolver中有一个属性叫resolveLazily,这个属性值代表是不是延迟解析文件上传,默认为false。最终返回的是一个DefaultMultipartHttpServletRequest的类。这里有一个重要的方法是:parseRequest,这个方法干的事是解析文件上传请求。它的底层是commons-fileupload那一套,不同的是Spring在获取FileItem之后,又进行了一下封装,封装为便于Spring框架整合。

下面我们接着看这一句话:

代码语言:javascript
复制
multipartRequestParsed = (processedRequest != request);

processedRequest是checkMultipart(request)这个方法返回的值,如果processedRequest和request不相等的话,则认为是文件上传的请求。

getHandler(processedRequest);

  • 为此请求返回HandlerExecutionChain。按顺序尝试所有的handler mapping

获取当前请求对应的处理类,在这个处理链中会包含对应的拦截器的信息。HandlerExecutionChain这个类中包含变和不变量的两部分内容

代码语言:javascript
复制
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	for (HandlerMapping hm : this.handlerMappings) {
		HandlerExecutionChain handler = hm.getHandler(request);
		if (handler != null) {
			return handler;
		}
	}
	return null;
}

循环handlerMappings,然后获取对应的执行链,只要找到一个对应的执行链就返回

SpringMVC默认加载三个请求处理映射类:

  • RequestMappingHandlerMapping
  • SimpleUrlHandlerMapping
  • BeanNameUrlHandlerMapping

这三个类有一个共同的父类:AbstractHandlerMapping。

hm.getHandler(request)这个getHandler方法在AbstractHandlerMapping中,它的子类都没有重写这个方法。

AbstractHandlerMethodMapping#getHandler

代码语言:javascript
复制
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 两个子类重写该方法:AbstractHandlerMethodMapping和AbstractUrlHandlerMapping
	Object handler = getHandlerInternal(request);
	// 如果没有找到的话,默认处理类
	if (handler == null) {
		handler = getDefaultHandler();
	}
	// 如果没有默认的处理类,返回null
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = getApplicationContext().getBean(handlerName);
	}
	// 包装为执行器链
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	// 是不是跨域请求
	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}

AbstractHandlerMethodMapping#getHandlerInternal

代码语言:javascript
复制
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		// 得到请求 url 的查询路径
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		}
		// 获取并发读锁
		this.mappingRegistry.acquireReadLock();
		try {
			// 得到查询路径对应的处理器方法
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

getHandlerExecutionChain

创建执行器链的内容:

代码语言:javascript
复制
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//判断handler是不是执行器链,如果不是创建一个执行器链
	HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
	
	String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
	//包装拦截器
	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		if (interceptor instanceof MappedInterceptor) {
			MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		}
		else {
			chain.addInterceptor(interceptor);
		}
	}
	return chain;
}

这个方法主要是创建执行器链,添加拦截器.

判断

代码语言:javascript
复制
if (mappedHandler == null || mappedHandler.getHandler() == null) {
	noHandlerFound(processedRequest, response);
	return;
}

如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常。 我们继续往下分析:

代码语言:javascript
复制
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter

获取处理适配器

  • SimpleControllerHandlerAdapter 适配SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping的映射的,也就是实现Controller接口的Handler
  • AbstractHandlerMethodAdapter 适配RequestMappingHandlerMapping,也就是我们常用的RequestMapping注解
  • HttpRequestHandlerAdapter 适配远程调用的
  • SimpleServletHandlerAdapter 适配Servlet实现类的

supports

supportsInternal

总是返回true ,因为任何方法参数和返回值类型会以某种方式加以处理。 不被任何HandlerMethodArgumentResolver识别的方法参数被解释为一个请求参数,如果它是一个简单的类型,或者作为模型属性否则。 没有任何HandlerMethodReturnValueHandler识别的返回值将被解释为一个模型属性

如果是GET请求,内容没有变化则直接返回

applyPreHandle

  • 应用已注册的preHandle拦截方法

handle()

mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 执行handle

代码语言:javascript
复制
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);

如果返回的mv不为null,并且没有设置view,则设置默认的view 应用postHandle拦截器。

processDispatchResult

代码语言:javascript
复制
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
		HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
 
	boolean errorView = false;
	if (exception != null) {
		if (exception instanceof ModelAndViewDefiningException) {
			mv = ((ModelAndViewDefiningException) exception).getModelAndView();
		}
		else {
			Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
			mv = processHandlerException(request, response, handler, exception);
			errorView = (mv != null);
		}
	}
	//返回的ModelAndView不为null
	// Did the handler return a view to render?
	if (mv != null && !mv.wasCleared()) {
		//解析页面
		render(mv, request, response);
		if (errorView) {
			WebUtils.clearErrorRequestAttributes(request);
		}
	}
	else {
	}
 
	if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
		// Concurrent handling started during a forward
		return;
	}
	// 调用处理拦截器的afterCompletion
	if (mappedHandler != null) {
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

如果

  • 出现异常,返回异常页面
  • 没有异常,ModelAndView不为null,则正常渲染页面,调用拦截器的afterCompletion方法
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-06-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 作用
  • DispatcherServlet的工作流程
  • checkMultipart
  • getHandler(processedRequest);
    • AbstractHandlerMethodMapping#getHandler
      • AbstractHandlerMethodMapping#getHandlerInternal
    • getHandlerExecutionChain
      • supports
      • supportsInternal
  • 判断
  • getHandlerAdapter
  • applyPreHandle
  • handle()
  • processDispatchResult
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档