DispatcherServlet源码剖析及工作原理

做基于Spring MVC框架的web开发,DispatcherServlet几乎是web.xml中必须配置的一个Servlet,绝大部分情况下也只需要配置这个Servlet就够用了。它有什么作用呢?顾名思义,它是一个派发器,即对任意一个web请求都会根据一定的规则派发到对应的处理器上处理,并最终将结果返回。它实现了Request to Handler的路由,而我们只需要实现处理器的逻辑,大大简化了开发代码复杂度和耦合度。
阅读DispatcherServlet源码时,需要理解其中的几个重要概念:

  • HandlerMapping
  • HandlerAdapter
  • HandlerExceptionResolver
  • ViewResolver
  • MultipartResolver
  • LocaleResolver
  • ThemeResolver

这几个概念稍后会做详细介绍。

该类主要包含两个逻辑:

  • DispatcherServlet的初始化
  • DispatcherServlet对web请求的处理
DispatcherServlet的初始化

DispatcherServlet继承自FrameworkServlet, 初始化依赖于后者的初始化。在《FrameworkServlet初始化》一文中,讲述了FrameworkServlet初始化过程,最终会调用FrameworkServlet的initServletBean()方法,该方法会调用initWebApplicationContext()方法,用于初始化web application context并加载配置,这个操作完毕后,会调用onRefresh(ApplicationContext context)方法,而这个方法是子类需要实现的。
DispatcherServlet类实现的onRefresh(ApplicationContext context)方法完成了一件事,就是从用户定义的application context中加载自定义的bean或者从默认文件中加载各种bean,用于初始化HandlerMapping, HandlerAdapter, HandlerExceptionResolver等bean对象。源码如下:

	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	//  Initialize the strategy objects that this servlet uses.
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

这些方法默认会先从已经初始化好的context中根据默认的beanName获取(调用getBean(String name, Class<T> requiredType)方法),如果没有则设置为null(对于那些不是必须的类实例,比如文件上传需要的multipartResolver)或从DispatcherServlet.properties文件中读取默认的实现类,并实例化。

下面我们来看看可选和必须配置的代码示例。

来看看initMultipartResolver(context)的实现。

	private void initMultipartResolver(ApplicationContext context) {
		try {
			this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.multipartResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Default is no multipart resolver.
			this.multipartResolver = null;
			if (logger.isTraceEnabled()) {
				logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
			}
		}
	}

MULTIPART_RESOLVER_BEAN_NAME是给定的默认beanName, 默认为multipartResolver。如果我们的application context中配置有实现了MultipartResolver的bean,那么就将该MultipartResolver实例对象赋值给multipartResolver对象。如果我们没有配置,就会走catch分支,这里仅仅将multipartResolver赋值null,并没有抛出异常等,所以这是一个optional的配置。

再来看看一个必须配置的一个bean对象的实例化,比如initHandlerMappings(ApplicationContext context)方法。

	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
        // 这是一个配置开关,用来决定是否是否需要从父类中查找所有的HandlerMapping实现类
		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<>();
		}
	}

initHandlerMappings()方法的主要逻辑可分两步:

  1. 从现有的application context配置文件中查找实现了HandlerMapping的已经初始化了bean。
  2. 在第一步的基础上判断是否还需要读取缺省的bean配置,如果第一步里没找到用户手工配置的bean信息,那么就从DispatcherServlet.properties文件中指定的类作为缺省的HandlerMapping实现类,并实例化。

我们来看看DispatcherServlet.properties文件如何定义的。

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

比如HandlerMapping的默认实现类就是org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping。

其他方法的初始化也是类似步骤。

至此,DispatcherServlet的初始化完毕了,所需要的各种bean都已经通过用户自定义的application context文件制定或通过缺省配置文件已经初始化。

DispatcherServlet对web请求的处理

任何一个请求都会有对应的一个servlet来处理,是通过调用其service(ServletRequest req, ServletResponse res)方法来实现的,这个方法实现了对一个请求的具体响应处理逻辑。DispatcherServlet处理一个请求的整个调用链可用下图来描述:
Dispatcher处理请求调用链
总结起来,依次经过了以下调用:

  1. 调用HttpServlet.service(ServletRequest, ServletResponse)方法。
  2. 调用FrameworkServlet.service(HttpServletRequest, HttpServletResponse)方法。
  3. 调用HttpServlet.service(HttpServletRequest, HttpServletResponse)方法。
  4. 调用FrameworkServlet.doXXX()方法。
  5. 调用DispatcherServlet.doService()方法。
  6. 调用DispatcherServlet.doDispatch()方法。

下图是通过debug设置断点的方式展示的请求调用堆栈:
调用堆栈
doDispatch()方法包含了真正处理一个请求的核心逻辑。执行步骤包含以下几步:

  1. 根据已初始化的handlerMappings和request,获取该请求对应的HandlerExecutionChain对象;
  2. 依次调用HandlerExecutionChain维护的interceptors, 执行handlerInterceptor的preHandle()方法。这里需要强调的是,如果某个interceptor.preHandle()处理失败,就直接短路,所以handler是不会被执行的,而是通过exceptionResolver处理异常并返回给客户端;
  3. 如果interceptor都执行成功,通过handlerAdaptors获取到该请求对应的HandlerAdaptor, 并执行handler;
  4. 执行完毕后,执行interceptor.postHandle()方法,如果需要返回view, 则配置ModelAndView, 并渲染view对象。

附上源码中对DispatcherServlet的解释,对理解很有帮助。

Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities.
This servlet is very flexible: It can be used with just about any workflow, with the installation of the appropriate adapter classes. It offers the following functionality that distinguishes it from other request-driven web MVC frameworks:
It is based around a JavaBeans configuration mechanism.
It can use any HandlerMapping implementation - pre-built or provided as part of an application - to control the routing of requests to handler objects. Default is org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping and org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping. HandlerMapping objects can be defined as beans in the servlet’s application context, implementing the HandlerMapping interface, overriding the default HandlerMapping if present. HandlerMappings can be given any bean name (they are tested by type).
It can use any HandlerAdapter; this allows for using any handler interface. Default adapters are org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter, org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter, for Spring’s org.springframework.web.HttpRequestHandler and org.springframework.web.servlet.mvc.Controller interfaces, respectively. A default org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter will be registered as well. HandlerAdapter objects can be added as beans in the application context, overriding the default HandlerAdapters. Like HandlerMappings, HandlerAdapters can be given any bean name (they are tested by type).
The dispatcher’s exception resolution strategy can be specified via a HandlerExceptionResolver, for example mapping certain exceptions to error pages. Default are org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver, org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver, and org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver. These HandlerExceptionResolvers can be overridden through the application context. HandlerExceptionResolver can be given any bean name (they are tested by type).
Its view resolution strategy can be specified via a ViewResolver implementation, resolving symbolic view names into View objects. Default is org.springframework.web.servlet.view.InternalResourceViewResolver. ViewResolver objects can be added as beans in the application context, overriding the default ViewResolver. ViewResolvers can be given any bean name (they are tested by type).
If a View or view name is not supplied by the user, then the configured RequestToViewNameTranslator will translate the current request into a view name. The corresponding bean name is “viewNameTranslator”; the default is org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator.
The dispatcher’s strategy for resolving multipart requests is determined by a MultipartResolver implementation. Implementations for Apache Commons FileUpload and Servlet 3 are included; the typical choice is org.springframework.web.multipart.commons.CommonsMultipartResolver. The MultipartResolver bean name is “multipartResolver”; default is none.
Its locale resolution strategy is determined by a LocaleResolver. Out-of-the-box implementations work via HTTP accept header, cookie, or session. The LocaleResolver bean name is “localeResolver”; default is org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver.
Its theme resolution strategy is determined by a ThemeResolver. Implementations for a fixed theme and for cookie and session storage are included. The ThemeResolver bean name is “themeResolver”; default is org.springframework.web.servlet.theme.FixedThemeResolver.
NOTE: The @RequestMapping annotation will only be processed if a corresponding HandlerMapping (for type-level annotations) and/or HandlerAdapter (for method-level annotations) is present in the dispatcher. This is the case by default. However, if you are defining custom HandlerMappings or HandlerAdapters, then you need to make sure that a corresponding custom RequestMappingHandlerMapping and/or RequestMappingHandlerAdapter is defined as well - provided that you intend to use @RequestMapping.
A web application can define any number of DispatcherServlets. Each servlet will operate in its own namespace, loading its own application context with mappings, handlers, etc. Only the root application context as loaded by org.springframework.web.context.ContextLoaderListener, if any, will be shared.
As of Spring 3.1, DispatcherServlet may now be injected with a web application context, rather than creating its own internally. This is useful in Servlet 3.0+ environments, which support programmatic registration of servlet instances. See the DispatcherServlet(WebApplicationContext) javadoc for details.

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值