栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringMVC源码--DispatcherServlet 初始化

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringMVC源码--DispatcherServlet 初始化

文章目录
        • DispatcherServlet初始化
          • 初始化成员变量 - handlerMappings
            • 默认实现
          • 初始化成员变量 - multipartResolver

DispatcherServlet初始化

SpringMVC运行在Servlet容器中(tomcat、jetty),是对servlet的封装, 所以整个生命周期与Servlet是有关系的。

【Servlet】

public void init(ServletConfig config)

The servlet container calls the init method exactly once after instantiating the servlet. The init method must complete successfully before the servlet can receive any requests.

Servlet 容器会精确调用一次servlet的init 方法,用于执行一些初始化的操作。

DispatcherServlet是SpringMVC中的核心类,继承于Servlet,故在Servlet容器中会触发一次init方法。

调用链如下:

DispatcherServlet.onRefresh(ApplicationContext context)

	protected void onRefresh(ApplicationContext context) {
	    // 初始化SpringMVC组件
		initStrategies(context);
	}

initStrategies 方法是DispatcherServlet 成员变量初始化的核心代码。

	protected void initStrategies(ApplicationContext context) {
	    // 初始化form-data 中的文件上传
		initMultipartResolver(context);
		// 国际化
		initLocaleResolver(context);
		// 主题
		initThemeResolver(context);
		// 映射器
		initHandlerMappings(context);
		// 处理器
		initHandlerAdapters(context);
		// 异常处理器
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		// 视图解析器
		initViewResolvers(context);
		initFlashMapManager(context);
	}
初始化成员变量 - handlerMappings

执行 initStrategies中的方法会DispatcherServlet中的成员变量赋值。

我们以 HandlerMapping初始化为例, HandlerAdapter, HandlerExceptionResolver 、ViewResolver都是这样的处理逻辑。

	
	@Nullable
	private List handlerMappings;

对应的方法

	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
      
		if (this.detectAllHandlerMappings) {
			Map matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			//......................
		}

		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		}
	}

detectAllHandlerMappings 默认为true, 代码会进入if分支。

private boolean detectAllHandlerMappings = true

if分支逻辑很简单, 从IOC容器中获取类型为HandlerMapping.class 的Bean集合。

默认实现

回想之前的项目开发,我们没有配置HandlerMapping为什么依旧可以处理请求呢? 这是因为SpringMVC提供了默认HandlerMapping。

if (this.handlerMappings == null) {
	this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}

当在IOC容器中获取不到类型为HandlerMapping 的bean时,就会走默认的处理逻辑。

protected  List getDefaultStrategies(ApplicationContext context, Class strategyInterface) {
		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		//...... 创建实例并返回	
}

defaultStrategies 又是什么东西啊?

private static final Properties defaultStrategies;

看定义发现是Properties , 所以defaultStrategies 会加载一个properties配置文件。

	ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
	
	defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);

以下为截取的部分数据:

  • key: org.springframework.web.servlet.HandlerMapping,
  • value 是 HandlerMapping 字符串,多个逗号隔开。
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

看到这种全限定的类名,我们应该可以猜到, 可以通过Class.forName(“”) , cls.newInstance() 创建实例。

但是上面方式的创建有一个问题就是,创建的对象不受Spring IOC容器的管理,这样如果存在初始化方法也需要手动触发。

SpringMVC 采用的方式是将这些Bean的创建交给IOC 容器,故调用AutowireCapableBeanFactory的createBean 方式创建实例。 创建Bean实例的流程特别复杂,就不展开了。

Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
protected Object createDefaultStrategy(ApplicationContext context, Class clazz) {
	return context.getAutowireCapableBeanFactory().createBean(clazz);
}
初始化成员变量 - multipartResolver

multipartResolver 、localeResolver、themeResolver 这些初始化逻辑相同。从容器中获取 指定名称、指定类型的bean。

	private void initMultipartResolver(ApplicationContext context) {
		try {
			this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
		}
		catch (NoSuchBeanDefinitionException ex) {
			this.multipartResolver = null;
		}
	}
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";

如果容器中没有这样的bean,没有默认的处理方式,即不支持 主题、国际化、多媒体资源的处理。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1024345.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号