Spring MVC 「3」从DispatchServlet开始,一个请求的处理流程

298 阅读5分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

01-DispatchServlet

DispatchServlet是Spring MVC的核心,其遵循前端控制器设计模式。它是Spring实现的一个servlet,在servlet容器中,负责将请求路由到其他的组件当中。

Java ™ Servlet Specification Version 3.0中对servlet和servlet容器的定义如下:

A servlet is a Java™ technology-based Web component, managed by a container, that generates dynamic content.

The servlet container is a part of a Web server or application server that provides the network services over which requests and responses are sent, decodes MIME-based requests, and formats MIME-based responses. A servlet container also contains and manages servlets through their lifecycle.

01.1-DispatcherServlet处理请求的基本流程

[1] 描述了一个请求到达DispatchServlet后的基本流程:

  1. 请求离开浏览器到达DispatchServlet
  2. DispatchServlet负责根据请求携带的URL信息,确定请求被路由到哪里
  3. DispatchServlet将请求转交给2.中选中的控制器
  4. 控制器返回模型和视图名给DispatchServlet
  5. DispatchServlet根据视图名确定具体的视图实现
  6. 具体的实现将4.中的逻辑视图渲染成真正的视图
  7. 视图被返回给浏览器

了解DispatchServlet处理请求的基本流程后,不纠结具体的实现细节,让我们先来看一下在Spring MVC中如何配置DispatchServlet?

01.2-Spring MVC中配置DispatcherServlet

  1. 通过实现WebApplicationInitializer接口的方式

    public class MyWebApplicationInitializer implements WebApplicationInitializer {
    
        @Override
        public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
            // Load Spring web application configuration
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(AppConfig.class);
    
            // Create and register the DispatcherServlet
            DispatcherServlet servlet = new DispatcherServlet(context);
            ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
            registration.setLoadOnStartup(1);
            registration.addMapping("/app/*");
        }
    
    }
    

    Servlet容器在启动时,会通过SPI机制,回调到onStartup方法,具体参考 Spring MVC 「2」WebApplicationInitializer的工作原理

    回到上面的代码,在onStartup方法中,我们创建了一个DispatcherServlet,并将其注册在servletContext中,并将/app/*与其绑定。

  2. 通过继承AbstractAnnotationConfigDispatcherServletInitializer类的方式

    public class MyDispatchServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[0];
        }
    
        /**
         * 仅在需要应用程序上下文需要层次结构时需要
         * 如果应用仅需要一个上下文,则可以返回 null,
         * 而由getRootConfigClasses返回应用上下文
         */
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[0];
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[0];
        }
    }
    

    通过对AbstractAnnotationConfigDispatcherServletInitializer继承层次分析发现,其实现了WebApplicationInitializer接口,最终也归结为方式的另类形式,只不过省去了手动实例化DispatcherServlet的麻烦。

    Untitled

    与方式1相比,更推荐方式2,因为使用起来更方便。

  3. 通过实现WebMvcConfigurer接口的方式

    方式3与前面两种方式不太一样,具体不同之处逐个进行分析。

    首先,实现WebMvcConfigurer接口并不能实例化DispatcherServlet对象。

    其次,WebMvcConfigurer仅仅是为了让用户可以修改DispatcherServlet在处理请求时使用的各种对象,例如拦截器(addInterceptors())、视图解析器(configureViewResolvers())等等

    最后,使用Spring MVC时,通常会在配置文件上添加@EnableWebMvc注解。该注解会通过@Import引入另一个配置文件org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfigurationDelegatingWebMvcConfiguration中定义了setConfigurers方法:

    /**
    * 自动从容器中请求所有实现了 WebMvcConfigurer 接口的Bean
    * 所以,如果我们自己实现了 WebMvcConfigurer 接口,
    * 实现类会在最终添加到 configurers 中
    */
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
    	if (!CollectionUtils.isEmpty(configurers)) {
    		this.configurers.addWebMvcConfigurers(configurers);
    	}
    }
    

[1] Spring in Action [2] Specifications

02-结合源码分析DispatcherServlet处理请求的具体流程

02.1-DispatcherServlet初始化过程

当Servlet容器启动时,会通过SPI机制回调到org.springframework.web.SpringServletContainerInitializer#onStartup方法。在该方法中会遍历调用类路径下所有实现了WebApplicationInitializer接口的类的onStartup方法,最终程序流会进入我们自己的初始化类中。

以01.2节中介绍的第2中方式为例(即通过继承AbstractAnnotationConfigDispatcherServletInitializer来初始化DispatcherServlet) ,程序流会进入到我们的实现类中MyDispatchServletInitializer

// 该方法继承自抽象类 AbstractDispatcherServletInitializer 
public void onStartup(ServletContext servletContext) throws ServletException {
	/**
	* 实际上会调用继承自父类 AbstractContextLoaderInitializer 的 
	* registerContextLoaderListener方法,
	* 主要用来创建 rootApplicationContext 和创建监听器
	*/
	super.onStartup(servletContext); 
	/**
	* 主要用来创建 servletContext 及 DispatcherServlet对象
	* 将 DispatcherServlet 对象绑定到某个路径上(由getServletMappings()指定)
	*/
	this.registerDispatcherServlet(servletContext);
}

02.2-一个请求的处理流程

DispatcherServlet是Servlet接口的一个实现,其接口及类实现关系如下图所示:

image.png 当请求离开浏览器进入到tomcat以后,最终会被Servlet容器传递给DispatcherServlet继承来的方法HttpServlet#service中。在service方法中,根据请求中的HTTP方法,会交由特定的方法去处理:

String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
	lastModified = this.getLastModified(req);
	if (lastModified == -1L) {
		this.doGet(req, resp);
	} else {
		long ifModifiedSince = req.getDateHeader("If-Modified-Since");
		if (ifModifiedSince < lastModified) {
			this.maybeSetLastModified(resp, lastModified);
			this.doGet(req, resp);
		} else {
			resp.setStatus(304);
		}
	}
} else if (method.equals("HEAD")) {
	lastModified = this.getLastModified(req);
	this.maybeSetLastModified(resp, lastModified);
	this.doHead(req, resp);
} else if (method.equals("POST")) {
	this.doPost(req, resp);
} else if (method.equals("PUT")) {
	this.doPut(req, resp);
} else if (method.equals("DELETE")) {
	this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
	this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
	this.doTrace(req, resp);
} else {
	String errMsg = lStrings.getString("http.method_not_implemented");
	Object[] errArgs = new Object[]{method};
	errMsg = MessageFormat.format(errMsg, errArgs);
	resp.sendError(501, errMsg);
}

后续代码以doGet为例,请求被路由到继承来的FrameworkServlet#processRequest方法中,然后到达DispatcherServlet#doService方法中,在设置过一系列属性后(例如),请求最终到达DispatcherServlet#doDispatch方法中。

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;
				// 1. 从handlerMapping中查找处理器(遇到的第一个合适的)
				mappedHandler = this.getHandler(processedRequest);
				if (mappedHandler == null) {
					this.noHandlerFound(processedRequest, response);
					return;
				}
				// 2. 从适配器中找到第一个匹配的适配器
				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 ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				// 3. 处理请求
				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);
		}

	}
}

至此,请求的处理过程基本完毕。当然,我们依然忽略了许多的处理细节,这些将在后面的文章中详细分析。