SpringMVC 6+源码分析(二)DispatcherServlet实例化流程 1

发布于:2025-08-02 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、概述

上一章说到SpringMVC的启动流程,当Spring容器初始化完成后,将发送上下文监听事件给监听器。当FrameworkServlet收到事件通知以后将开始SpringMVC相关的业务操作,包括url的映射,参数的校验等,本章节将从源码的角度来讲解每一个业务流程。

二、实例化流程

当FrameworkServlet收到事件通知以后则调用 onRefresh()方法,该方法在DispatcherServlet类中,将对所有资源进行初始化,下面我们将一一讲解,initHandlerMappings(context) 为最重要的部分将单独讲解。

	/**
	 * 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) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

2.1 初始化文件上传解析器-initMultipartResolver

初始化文件解析器,当Spring应用启动时,如果配置了MultipartResolver(它用于处理文件上传(multipart/form-data请求)),DispatcherServlet会自动查找名为"multipartResolver"的bean。如果没有找到,就会抛出异常,提示没有定义该bean。

	/**
	 * Initialize the MultipartResolver used by this class.
	 * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
	 * no multipart handling is provided.
	 */
	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");
			}
		}
	}

同时解析器可以设置上传文件大小等参数,配置参数可以在配置文件中(spring.servlet.multipart),也可以使用注解的方式创建bean。

@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setMaxUploadSize(10000000);
    return multipartResolver;
}

2.2 初始化国际化解析器-initLocaleResolver

initLocaleResolver 负责初始化 LocaleResolver 对象,该对象用于解析 HTTP 请求中的区域设置信息(例如,通过 Accept-Language 头)。这有助于 Spring MVC 动态调整视图、消息源和其他本地化资源,以适应不同语言的用户。initLocaleResolver 首先检查 Spring IoC 容器,查找名为 localeResolver 的 bean 组件。如果找到对应的bean则返回,没有找到则使用Spring 默认的Resolver-AcceptHeaderLocaleResolver

private void initLocaleResolver(ApplicationContext context) {
		try {
			this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.localeResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
						"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
			}
		}
	}

2.3 初始化异常解析器- initHandlerExceptionResolvers

initHandlerExceptionResolvers(ApplicationContext context) 方法从 Spring 容器中查找所有类型为 HandlerExceptionResolver 的 Bean。如果未显式配置任何 HandlerExceptionResolver,则创建默认的异常解析器列表(包括 DefaultHandlerExceptionResolver 和 ExceptionHandlerExceptionResolver 等)。

private void initHandlerExceptionResolvers(ApplicationContext context) {
		this.handlerExceptionResolvers = null;

		if (this.detectAllHandlerExceptionResolvers) {
			// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
					.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
				// We keep HandlerExceptionResolvers in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
			}
		}
		else {
			try {
				HandlerExceptionResolver her =
						context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
				this.handlerExceptionResolvers = Collections.singletonList(her);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, no HandlerExceptionResolver is fine too.
			}
		}

		// Ensure we have at least some HandlerExceptionResolvers, by registering
		// default HandlerExceptionResolvers if no other resolvers are found.
		if (this.handlerExceptionResolvers == null) {
			this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

2.4 初始化视图名称翻译器-initRequestToViewNameTranslator

RequestToViewNameTranslator接口的主要作用是将请求(HttpServletRequest)转换为逻辑视图名(view name),当控制器方法返回的ModelAndView对象中未显式设置视图名时使用。
Spring默认使用的是DefaultRequestToViewNameTranslator,在实际开发场景中 我们很少去做这一类的配置。DefaultRequestToViewNameTranslator的作用是翻译,通俗点就是 将url(如:www.xxx/home/user/info.html)转化成 代码识别的 /home/user/info

private void initRequestToViewNameTranslator(ApplicationContext context) {
		try {
			this.viewNameTranslator =
					context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.viewNameTranslator);
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
						"': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
			}
		}
	}

2.5 初始化视图解析器-initViewResolvers

视图解析器的作用是将控制器返回的逻辑视图名解析为实际的视图对象(如JSP、Thymeleaf模板等)

private void initViewResolvers(ApplicationContext context) {
		this.viewResolvers = null;

		if (this.detectAllViewResolvers) {
			// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
			Map<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList<>(matchingBeans.values());
				// We keep ViewResolvers in sorted order.
				AnnotationAwareOrderComparator.sort(this.viewResolvers);
			}
		}
		else {
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}

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

三、总结

本文总结了几个主要的初始化流程,至于 初始化主题解析器、初始化处理器映射器流程将单独来讲解。


网站公告

今日签到

点亮在社区的每一天
去签到