这里主要是对Spring下DispatcherServlet处理http请求的流程做一个简单梳理!

DispatcherServlet

首先,我们先了解下原生java servlet处理http请求流程,大体上如下,

  1. 当客户端向服务器端发起http请求
  2. 请求会到达客户端服务器,如tomcat
  3. 服务器会根据请求路径匹配到对应的servtlet全限定名(xml文件中)
  4. 服务器会通过反射的方式实例化这个servlet(只会在第一次请求时实例化),并创建Servletrequest和ServletResponse两个对象,ServerletRequest用来封装请求对象,ServerletResponse用来封装处理结果,主要请求流程在service(ServerletRequest,ServerletResponse)中
    以上具体可以参考 转自

那么,在Spring中,http请求处理流程又是怎么样的呢?

同样的,也是Serverlet,是DispatcherServerlet,两者关系如下图

spring请求能终止吗_初始化


上图可以看出,DispatcherServlet是Servlet的唯一实现子类,并且被Spring容器管理,所以可以看出spring中是通过DispatcherServlet来处理Http请求,下面我们详细探讨下这个处理流程

整个流程可以分为两个阶段,初始化和处理请求

  1. 初始化: 请求到达服务器初始化,初始化DispatcherServlet,需要调用init()方法,调用了HttpServletBean中的init方法
@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		//注入servelet.xml的配置属性到bean中
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// 这里是实例化对象的核心方法
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

在看这个initServletBean()方法,其中initWebApplicationContext()为核心方法,用来创建DispatcherServlet的上下文对象

/**
	 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
	 * have been set. Creates this servlet's WebApplicationContext.
	 */
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

initWebApplicationContext()的代码如下,先获得ContextLoaderListener创建的根应用上下文,为DispatcherServlet创建自己的应用上下文,再刷新上下文对象

/**
	 * Initialize and publish the WebApplicationContext for this servlet.
	 * <p>Delegates to {@link #createWebApplicationContext} for actual creation
	 * of the context. Can be overridden in subclasses.
	 * @return the WebApplicationContext instance
	 * @see #FrameworkServlet(WebApplicationContext)
	 * @see #setContextClass
	 * @see #setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

onRefresh(wac),这个是刷新上下文对象的方法,初始化一些核心对象

/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
	    //初始化MultipartResolver,该Bean用于文件上传
		initMultipartResolver(context);
		//初始化LocaleResolver实例,这是国际化资源对象,例如我在中国,在访问服务器时,浏览器中设置的本地化类型会包含在HTML请求中,
		//发送给WEB服务器,如请求报文中的Accept-Language参数再比如我们看见某些网站上有英文.中文.繁体切换等按钮,这些可以
		//通过cookie/session/请求参数来切换本地化类型
		initLocaleResolver(context);
		//初始化图形解析器Bean,前后端分离的模式下用的相对较少了
		initThemeResolver(context);
		//当detectAllHandlerMappings为true时,从容器(以及祖先容器)获取所有类型为HandlerMapping的bean组件,记录到handlerMappings并排序;
		//当detectAllHandlerMappings为false时,从容器(以及祖先容器)获取名称为handlerMapping的bean组件,记录到handlerMappings,这种情况下handlerMappings中最多有一个元素;
		initHandlerMappings(context);
		//这个initHandlerMappings类似
		initHandlerAdapters(context);
		//当detectAllHandlerExceptionResolvers为true时,从容器(以及祖先容器)获取所有类型为HandlerExceptionResolver的bean组件,记录到handlerExceptionResolvers并排序;
		//当detectAllHandlerExceptionResolvers为false时,从容器(以及祖先容器)获取名称为handlerExceptionResolver的bean组件,记录到handlerExceptionResolvers,这种情况下handlerExceptionResolvers中最多有一个元素;
		initHandlerExceptionResolvers(context);
		//初始化RequestToViewNameTranslator实例对象.默认实现是DefaultRequestToViewNameTranslator
		initRequestToViewNameTranslator(context);
		//初始化ViewResolver实例,同initHandlerMappings(),获取所有ViewResolver类型的实例放在容器中
		initViewResolvers(context);
		//初始化FlashMapManager对象,默认实现是SessionFlashMapManager
		initFlashMapManager(context);
	}

以上是DispatcherServlet的基本初始化的流程,下面我们来看下DispatcherServlet处理http请求的一个流程

spring请求能终止吗_spring请求能终止吗_02