天天看点

啷个说明白:SpringMVC到底是怎么个工作流程啊?

作者:尚硅谷教育

01 SpringMVC简介

SpringMVC是一种基于Spring实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,使用了MVC架构模式的思想,将web层进行职责解耦,并管理应用所需对象的生命周期,为简化日常开发,提供了很大便利。

SpringMVC提供的核心组件包括请求分发器(DispatchServlet)、请求处理映射器(HandlerMapping)、请求处理适配器(Handler Adapter)、视图解析器(View Resolver) 、动作处理器(Controller)和模型视图对象(ModelAndView),通过以上各大组件的协同工作来完成客户端的请求响应工作,使得MVC架构的设计理念得以实现。

02 SpringMVC工作流程

我们先来看一下SpringMVC的流程图

啷个说明白:SpringMVC到底是怎么个工作流程啊?

(1)客户端向web服务器(如tomcat)发送一个http请求,web服务器对http请求进行解析,解析后的url地址如果匹配到DispatchServlet的映射路径(通过web.xml中的servlet-mapping配置),web容器就会将请求交给DispatchServlet处理

(2)DispatcherServlet接收到这个请求后,再对URL进行解析,得到请求资源标识符(URI)。然后调用相应方法得到的HandlerMapping对象,再根据URI,调用这个对象的相应方法获得Handler对象以及它对应的拦截器。(在这里只是获得了Handler对象,并不会操作它,在SpringMVC中,是通过HandlerAdapter对Handler进行调用、控制的)

(3)DispatcherServlet根据得到的Handler对象,选择一个合适的HandlerAdapter,创建其实例对象,执行拦截器中的preHandler()方法。

(4)在拦截器方法中,提取请求中的数据模型,填充Handler入参,所以所有准备工作都已做好,开始执行Handler(我们写的controller代码并不是能被直接执行,需要有刚才那些操作,才能转变为Handler被执行)。

(5)Handler执行完毕后返回一个ModelAndView对象给DispatcherServlet。

(6)这个ModleAndView只是一个逻辑视图,并不是真正的视图,DispatcherServlet通过ViewResolver视图解析器将逻辑视图转化为真正的视图(通俗理解为将视图名称补全,如加上路径前缀,加上.jsp后缀,能指向实际的视图)。

(7)DispatcherServlet通过Model将ModelAndView中得到的处数据解析后用于渲染视图。将得到的最终视图通过http响应返回客户端。

03 SpringMVC组件解析

Part.01 HandlerMapping

该组件在SpringMVC体系结构中有着举足轻重的地位,充当着url和Controller之间映射关系配置的角色。主要有三部分组成:HandlerMapping映射注册、根据url获取对应的处理器、拦截器注册。

啷个说明白:SpringMVC到底是怎么个工作流程啊?

在Spring MVC中,关于HandlerMapping的使用,主要包括两个部分:注册和查找。在HandlerMapping的实现中,持有一个handlerMap这样一个HashMap<String, Object>,其中key是http请求的path信息,value可以是一个字符串,或者是一个处理请求的HandlerExecutionChain,如果是String类型,则会将其视为Spring的bean名称。在HandlerMapping对象的创建中,IoC容器执行了一个容器回调方法setApplicationContext,在这个方法中调用initApplicationContext方法进行初始化,各个子类可以根据需求的不同覆写这个方法。关于handlerMap信息的注册就是在initApplicationContext方法中被执行的。

Part.02 HandlerAdapter

该组件在SpringMVC请求执行过程中包括两个部分,HandlerAdapter的注册和HandlerAdapter的执行。

啷个说明白:SpringMVC到底是怎么个工作流程啊?

(1)HandlerAdapter的注册:

DispatcherServlte会根据配置文件信息注册HandlerAdapter,如果在配置文件中没有配置,那么DispatcherServlte会获取HandlerAdapter的默认配置,如果是读取默认配置的话,DispatcherServlte会读取DispatcherServlte.properties文件,该文件中配置了三种HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。DispatcherServlte会将这三个HandlerAdapter对象存储到它的handlerAdapters这个集合属性中,这样就完成了HandlerAdapter的注册。

(2)HandlerAdapter的执行:

DispatcherServlte会根据handlerMapping传过来的controller与已经注册好了的HandlerAdapter一一匹配,看哪一种HandlerAdapter是支持该controller类型的,如果找到了其中一种HandlerAdapter是支持传过来的controller类型,那么该HandlerAdapter会调用自己的handle方法,handle方法运用java的反射机制执行controller的具体方法来获得ModelAndView,例如SimpleControllerHandlerAdapter是支持实现了controller接口的控制器,如果自己写的控制器实现了controller接口,那么SimpleControllerHandlerAdapter就会去执行自己写控制器中的具体方法来完成请求。

04 SpringMVC源码简单剖析

根据上边的图我们可以看到前端控制器在springMVC的系统中拥有了非常大的作用,那么我们就以此来入手。

啷个说明白:SpringMVC到底是怎么个工作流程啊?
  • 首先在它的关系图中我们可以看出,DispatcherServlet类继承FrameworkServlet类,而FrameworkServlet继承了HttpServletBean类,再往上就是HttpServlet类与Servlet类了。
  • 那么我们就可以得出DispatchServlet是一个servlet类,既然是一个Servlet那么就有一个核心方法:Service()方法,我们在DispatchServlet中找,发现没有,那我们继续去它的父类中找,于是发现了如下方法:

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());

if (HttpMethod.PATCH != httpMethod && httpMethod != null) {

super.service(request, response);

} else {

this.processRequest(request, response);

}

}

  • 它首先判断是不是PATCH请求,如果是的话执行父类的service()方法,如果不是执行 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 = this.buildLocaleContext(request);

RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();

ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));

this.initContextHolders(request, localeContext, requestAttributes);

try {

this.doService(request, response);

} catch (IOException | ServletException var16) {

failureCause = var16;

throw var16;

} catch (Throwable var17) {

failureCause = var17;

throw new NestedServletException("Request processing failed", var17);

} finally {

this.resetContextHolders(request, previousLocaleContext, previousAttributes);

if (requestAttributes != null) {

requestAttributes.requestCompleted();

}

if (this.logger.isDebugEnabled()) {

if (failureCause != null) {

this.logger.debug("Could not complete request", (Throwable)failureCause);

} else if (asyncManager.isConcurrentHandlingStarted()) {

this.logger.debug("Leaving response open for concurrent processing");

} else {

this.logger.debug("Successfully completed request");

}

}

this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);

}

}

  • 这里边的代码主要是执行一些控制器等等,可以不用了解太多,我们主要是了解其执行流程。在其中我们可以看到 this.doService(request, response);方法,来执行一些事务,那么我们继续追踪:

protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

  • 我们发现它是调用了子类:DispatcherServlet类中的方法,继续追踪:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

if (this.logger.isDebugEnabled()) {

String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";

this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");

}

Map<String, Object> attributesSnapshot = null;

if (WebUtils.isIncludeRequest(request)) {

attributesSnapshot = new HashMap();

Enumeration attrNames = request.getAttributeNames();

label112:

while(true) {

String attrName;

do {

if (!attrNames.hasMoreElements()) {

break label112;

}

attrName = (String)attrNames.nextElement();

} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

attributesSnapshot.put(attrName, request.getAttribute(attrName));

}

}

request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());

request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);

request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);

request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());

if (this.flashMapManager != null) {

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 {

this.doDispatch(request, response);

} finally {

if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {

this.restoreAttributesAfterInclude(request, attributesSnapshot);

}

}

}

  • 我们看到它首先设置了一些request属性,然后执行了一个this.doDispatch(request, response)方法,至此我们算是找到了它的核心执行代码:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;

HandlerExecutionChain mappedHandler = null;

boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {

try {

ModelAndView mv = null;

Object dispatchException = null;

try {

//这个方法下面有单独说明

processedRequest = this.checkMultipart(request);

multipartRequestParsed = processedRequest != request;

//这个方法下面有单独说明

mappedHandler = this.getHandler(processedRequest);

if (mappedHandler == null) {

this.noHandlerFound(processedRequest, response);

return;

}

//这个方法下面有单独说明

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

String method = request.getMethod();

boolean isGet = "GET".equals(method);

if (isGet || "HEAD".equals(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

if (this.logger.isDebugEnabled()) {

this.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;

}

//这个方法下面有单独说明

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

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

this.applyDefaultViewName(processedRequest, mv);

//这个方法下面有单独说明

mappedHandler.applyPostHandle(processedRequest, response, mv);

} catch (Exception var20) {

dispatchException = var20;

} catch (Throwable var21) {

dispatchException = new NestedServletException("Handler dispatch failed", var21);

}

//这个方法下面有单独说明

this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

} catch (Exception var22) {

this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);

} catch (Throwable var23) {

this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));

}

} finally {

if (asyncManager.isConcurrentHandlingStarted()) {

if (mappedHandler != null) {

mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

}

} else if (multipartRequestParsed) {

this.cleanupMultipart(processedRequest);

}

}

}

  • 这段代码可以分成四个部分:
  • ①查找Handler对应的HandlerMapping部分:

try {

processedRequest = this.checkMultipart(request);

multipartRequestParsed = processedRequest != request;

mappedHandler = this.getHandler(processedRequest);

if (mappedHandler == null) {

this.noHandlerFound(processedRequest, response);

return;

}

  • 它首先判断request请求是不是二进制请求,然后执行this.getHandler(processedRequest);方法。那么我们来了解一下这个方法:
  • ②this.getHandler(processedRequest)部分

@Nullable

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

if (this.handlerMappings != null) {

Iterator var2 = this.handlerMappings.iterator();

while(var2.hasNext()) {

HandlerMapping hm = (HandlerMapping)var2.next();

if (this.logger.isTraceEnabled()) {

this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");

}

HandlerExecutionChain handler = hm.getHandler(request);

if (handler != null) {

return handler;

}

}

}

return null;

}

  • 首先:我们发现它获取了一个HandlerMapping的迭代器,然后查找每一个HandlerMapping。那么它为什么要先去查找HandlerMapping呢?那是因为我们在配置Controller的时候有两种方式:xml和注解的方式,所以它先去找到HandlerMapping,然后根据HandlerMapping去找Handler,就是hm.getHandler(request);这个方法来查找Handler。
  • ③查找HandlerAdapter部分:

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

String method = request.getMethod();

boolean isGet = "GET".equals(method);

if (isGet || "HEAD".equals(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

if (this.logger.isDebugEnabled()) {

this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);

}

if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {

return;

}

}

④在拿到HandlerMapping之后,开始查找HandlerAdapter,就是ha.getLastModified(request, mappedHandler.getHandler());来查找。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

if (this.handlerAdapters != null) {

Iterator var2 = this.handlerAdapters.iterator();

while(var2.hasNext()) {

HandlerAdapter ha = (HandlerAdapter)var2.next();

if (this.logger.isTraceEnabled()) {

this.logger.trace("Testing handler adapter [" + ha + "]");

}

if (ha.supports(handler)) {

return ha;

}

}

}

throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");

}

它的查找方式与之前查找HandlerMapping类似。

  • 拦截器执行部分:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

HandlerInterceptor[] interceptors = this.getInterceptors();

if (!ObjectUtils.isEmpty(interceptors)) {

for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {

HandlerInterceptor interceptor = interceptors[i];

if (!interceptor.preHandle(request, response, this.handler)) {

this.triggerAfterCompletion(request, response, (Exception)null);

return false;

}

}

}

return true;

}

以上一小段代码就是获取拦截器然后执行。

  • 再接下来就是最重要的Hanler执行部分,它返回的对象是mv,就是ModelAndView对象

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

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

this.applyDefaultViewName(processedRequest, mv);

mappedHandler.applyPostHandle(processedRequest, response, mv);

} catch (Exception var20) {

dispatchException = var20;

} catch (Throwable var21) {

dispatchException = new NestedServletException("Handler dispatch failed", var21);

}

//这个方法下面有说明

this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

它首先在 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 执行了handler并返回了一个视图;然后通过 this.applyDefaultViewName(processedRequest, mv); 在我们没有设置视图名称的时候返回默认视图名;然后通过 this.applyDefaultViewName(processedRequest, mv); 获取到项目的拦截器,然后判断拦截器是否存在,存在则对该请求进行测试(postHandler),如果有一个拦截器不成功,则请求失败。最后来到视图解析和渲染的执行方法:this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {

boolean errorView = false;

if (exception != null) {

if (exception instanceof ModelAndViewDefiningException) {

this.logger.debug("ModelAndViewDefiningException encountered", exception);

mv = ((ModelAndViewDefiningException)exception).getModelAndView();

} else {

Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;

mv = this.processHandlerException(request, response, handler, exception);

errorView = mv != null;

}

}

if (mv != null && !mv.wasCleared()) {

//这个方法下面有说明

this.render(mv, request, response);

if (errorView) {

WebUtils.clearErrorRequestAttributes(request);

}

} else if (this.logger.isDebugEnabled()) {

this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");

}

if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

if (mappedHandler != null) {

mappedHandler.triggerAfterCompletion(request, response, (Exception)null);

}

}

}

它首先判断一下是否异常,如果出现异常,判断异常是否是ModelAndViewDefiningException的实例,如果是就直接调用getModelAndView方法返回一个mv,否则调用processHanlderException方法初始化mv对象;如果没有异常出现,那么判断mv是否为空,为空抛出异常,不为空则判断mv,wasCleared()方法返回的值是否为真;判断完成以后,执行了 this.render(mv, request, response);方法。

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {

Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();

response.setLocale(locale);

String viewName = mv.getViewName();

View view;

if (viewName != null) {

view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);

if (view == null) {

throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");

}

} else {

view = mv.getView();

if (view == null) {

throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");

}

}

if (this.logger.isDebugEnabled()) {

this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");

}

try {

if (mv.getStatus() != null) {

response.setStatus(mv.getStatus().value());

}

view.render(mv.getModelInternal(), request, response);

} catch (Exception var8) {

if (this.logger.isDebugEnabled()) {

this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var8);

}

throw var8;

}

}

它首先设置了视图的编码格式,然后通过 view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);进行了视图解析:

public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

if (this.logger.isTraceEnabled()) {

this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);

}

Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);

this.prepareResponse(request, response);

this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);

}

它首先通过 Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);获取了传入的model的map集合。然后通过 this.prepareResponse(request, response);设置响应头的两个参数,最后通过 this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);将mergeModel合到请求里.

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

this.exposeModelAsRequestAttributes(model, request);

this.exposeHelpers(request);

String dispatcherPath = this.prepareForRendering(request, response);

RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);

if (rd == null) {

throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");

} else {

if (this.useInclude(request, response)) {

response.setContentType(this.getContentType());

if (this.logger.isDebugEnabled()) {

this.logger.debug("Including resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");

}

rd.include(request, response);

} else {

if (this.logger.isDebugEnabled()) {

this.logger.debug("Forwarding to resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");

}

rd.forward(request, response);

}

}

}

它首先进行了 this.exposeModelAsRequestAttributes(model, request);操作:

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {

model.forEach((modelName, modelValue) -> {

if (modelValue != null) {

request.setAttribute(modelName, modelValue);

if (this.logger.isDebugEnabled()) {

this.logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this.getBeanName() + "'");

}

} else {

request.removeAttribute(modelName);

if (this.logger.isDebugEnabled()) {

this.logger.debug("Removed model object '" + modelName + "' from request in view with name '" + this.getBeanName() + "'");

}

}

});

}

由此可见,model中的数据,最终是放在request域中的。

然后他们进行了RequestDispatcher 对象配置,就是选择它要进行的操作了,是使用include还是forward进行跳转。至此,视图的解析与渲染工作都完成了,并且也发送到了前端页面,完成了一个请求的整个流程。

05 总结

SpringMVC的总体执行流程的源码解析就为以上描述,我们发现SpringMVC最主要的控制器类DispatcherServlet,它在项目的web.xml中配置拦截地址。当我们发送请求时就可以进入到以上代码中,从而完成整个请求处理。