前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringFramework之DispatcherServlet的初始化简析

SpringFramework之DispatcherServlet的初始化简析

作者头像
克虏伯
发布2019-04-15 14:35:00
6290
发布2019-04-15 14:35:00
举报

注:SpringFramework的版本是4.3.x。

1.DispatcherServlet的类继承图

                                                           图1 DispatchServlet的类继承图

  •     FrameworkServlet通过继承,覆写了HttpServlet的doXX方法,将所有的http请求都转给processRequest(HttpServletRequest,HttpServletResponse),之后调用DispatchServlet的doService(HttpServletRequest,HttpServletResponse)方法。

2.DispatcherServlet的init(...)方法调用时序图

    调用Servlet的init方法初始化Servlet,时序图如下所示:

                                                          图2 调用Servlet的init的时序图

    这个init(...)是Servlet的方法,在容器container初始化Servlet时调会调用Servlet的init(...)方法。DispatcherServlet中通过init(...)来生成需要的webApplicationContext。

    2.1 DispatcherServlet的onRefresh方法

       图2中步骤6中的onRefresh(...)的实现在DispatcherServlet中实现,源码如下List-1所示,initStrategies方法中对DispatcherServlet的属性进行了初始化。

List-1 DispatcherServlet的onRefresh方法中的源码

代码语言:javascript
复制
@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);
}
  •     步骤7的方法体为空,不用管。
  •     方法initMultipartResolver、initLocaleResolver、initThemeResolver、initRequestToViewNameTranslator、initFlashMapManager的处理方式几乎都一样。
  •     方法initHandlerMappings、initHandlerAdapters、initHandlerExceptionResolvers的处理方式几乎都一样。

    2.2 DispatcherServlet的initHandlerMappings方法实现

    着重来看下方法initHandlerMappings的实现,方法中的源码如下List-2所示

List-2 initHandlerMappings方法的源码

代码语言:javascript
复制
private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	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<HandlerMapping>(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.isDebugEnabled()) {
			logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
		}
	}
}

    对LIst-2的说明:

  •     首先从applicationContext中取出所有的HandlerMapping,得到的是个集合,再对集合进行排序。所以我们可以自定义HandlerMapping,然后注册到applicationContext中,这样在spring就会自动使用我们自定义的HandlerMapping了。
  •     如果从applicationContext中得到的HandlerMapping集合为空,则创建默认的HandlerMapping。

    来分析getDefaultStrategies的源码,如下List-3所示。

List-3 getDefaultStrategies的源码

代码语言:javascript
复制
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<T>(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(
						"Error loading DispatcherServlet's default strategy class [" + className +
								"] for interface [" + key + "]: problem with class file or dependent class", err);
			}
		}
		return strategies;
	}
	else {
		return new LinkedList<T>();
	}
}

    defaultStrategies是DispatcherServlet的一个属性,它的初始化如下List-4所示,

List-4 defaultStrategies的初始化源码

代码语言:javascript
复制
private static final Properties defaultStrategies;

static {
	// Load default strategy implementations from properties file.
	// This is currently strictly internal and not meant to be customized
	// by application developers.
	try {
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
	}
}

    DEFAULT_STRATEGIES_PATH的值是DispatcherServlet.properties,我们来看下这个文件里面的内容,如下图3所示,可以看到默认情况下HandlerMapping定义了俩个,即BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping,所以List-3中的createDefaultStrategy方法会将BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping创建为bean,并注册到applicationContext中。

                                            图3 文件DispatcherServlet.properties的内容

    List-3中的createDefaultStrategy(context, clazz),代码如下List-4所示。通过ApplicationContext和Class<?>,就可以创建bean了,这种手动创建的方式可以学习下。

List-4 方法createDefaultStrategy的源码

代码语言:javascript
复制
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
	return context.getAutowireCapableBeanFactory().createBean(clazz);
}

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018/06/09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.DispatcherServlet的类继承图
  • 2.DispatcherServlet的init(...)方法调用时序图
    •     2.1 DispatcherServlet的onRefresh方法
      •     2.2 DispatcherServlet的initHandlerMappings方法实现
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档