spring mvc 源码分析之spring mvc初始化

spring mvc 源码分析

前言

我们知道spring mvc要实现web能力,主要依靠九大组件。因此spring mvc初始化,其实就是对这九大组件的初始化。在DispatcherServlet中定义了九个属性,每⼀个属性都对应⼀种组件。因此spring mvc初始化就约等于DispatcherServlet这个类的初始化,下面我们通过分析DispatcherServlet源码,一起来看一看spring mvc 的初始化到底干了什么?

一、DispatcherServlet中定义的九个属性

九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如ViewResolver、HandlerAdapter等都是接⼝

/** MultipartResolver used by this servlet. */
// 多部件解析器-- 处理文件上传的
@Nullable
private MultipartResolver multipartResolver;

-------------------------------------------------------------------

/** LocaleResolver used by this servlet. */
// 区域化 国际化解析器
@Nullable
private LocaleResolver localeResolver;

-------------------------------------------------------------------

/** ThemeResolver used by this servlet. */
// 主题解析器
@Nullable
private ThemeResolver themeResolver;

-------------------------------------------------------------------

/** List of HandlerMappings used by this servlet. */
// 处理器映射器组件   大致就是请求路径与handler的映
@Nullable
private List<HandlerMapping> handlerMappings;

-------------------------------------------------------------------

/** List of HandlerAdapters used by this servlet. */
// 处理器适配器组件  能够调用handler的适配器
@Nullable
private List<HandlerAdapter> handlerAdapters;

-------------------------------------------------------------------

/** List of HandlerExceptionResolvers used by this servlet. */
// 异常解析器组件
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;

-------------------------------------------------------------------

/** RequestToViewNameTranslator used by this servlet. */
// 默认视图名转换器组件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;

-------------------------------------------------------------------

/** FlashMapManager used by this servlet. */
// flash属性管理组件
@Nullable
private FlashMapManager flashMapManager;

-------------------------------------------------------------------

/** List of ViewResolvers used by this servlet. */
// 视图解析器
@Nullable
private List<ViewResolver> viewResolvers;

二、九⼤组件的初始化时机

我们知道spring 的初始化核心方法是refresh() 不清楚的小伙伴可以看下spring 源码专栏,因此我们可以猜想是不是在spring mvc初始化时 是不是也调用了一个类似的方法,最后在DispatcherServlet类中找到onRefresh(ApplicationContext context)方法,废话不多说,直接断点上源码。

onRefresh(ApplicationContext context)

@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);
		// 初始化FlashMap管理器
		initFlashMapManager(context);
	}

**在启动spring mvc 时在onRefresh打上端点,但是启动好后并没有走这个方法。难道是分析错了? **

重点spring mvc 在程序启动时是不会初始化的,只有当第一次请求来临时 才会初始化,这跟spring是不同的。并且初始化后存放的容器也不是同一个。这就会存在一个经典的父子容器问题。最后我们会讨论这个问题。
在这里插入图片描述

开始第一次请求

请求最开始搭建的源码分析案例 http://localhost:8080/demo/handle01?name=zhangsan

第一次请求时,通过下图可知 进入端点了。那么就刚好证实了spring mvc是在第一次请求时初始化的。
通过调用栈可以知道:首先调用 HttpServletBean的initServletBean()方法,后续去创建WebApplicationContext,最后调用了AbstractApplicationContext的refresh()方法中的finishRefresh() 去初始化九大件
在这里插入图片描述

初始化处理器映射 initHandlerMappings(context);

首先判断是否需要全部去查找,按照HandlerMapping类型去查找,或者在ioc中按照固定名字查找,没有就使用默认handlerMapping

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			// 找到所有的HandlerMappings 按照HandlerMapping类型去查找
			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 {
				// 否则在ioc中按照固定名字查找
				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) {
			// 如果都没有找到按照默认的策略生成  实例化handlerMapping
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

获取到了三个默认的handlerMapping
== defaultStrategies.getProperty(key) 获取的是默认文件DispatcherServlet.properties.里配置的类的全路径==
在这里插入图片描述

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.function.support.RouterFunctionMapping

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.function.support.HandlerFunctionAdapter


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

后面所有的组件初始化 都是类似的处理,没有自定义的,就读取DispatcherServlet.properties中配置的默认实现方法,通过反射实例化存入内存中。

注意:多部件解析器的初始化必须按照id注册对象(multipartResolver)

	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");
			}
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起风了 收衣服

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值