SpringMvc学习日记-DispatchServlet初始化流程

        从本篇开始分析SpringMvc的源码,版本5.3.6。SpringMvc提供一个类DispatcherServlet,是处理http请求入口。该类本质上是一个Servlet,可通过UML得知,既然是Servlet那么就按照Servlet生命周期去分析源码就好了。  

一、初始化整体流程图

         初始化整体流程,入口肯定是Servlet#init(ServletConfig)方法,沿着这个思路去看源码,可以得大体框架图:

注:DispatcherServlet继承了FrameworkServlet类,所以很多东西是父类中完后的

以xml配置方式,进行源码解析,web.xml配置如下:

<!-- servlet -->
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc04.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

二、HttpServletBean#init 

该方法代码如下所示:

@Override
public final void init() throws ServletException {

    // Set bean properties from init parameters.
    // 解析web.xml中init-param标签,存储到PropertyValues
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {//通过xml方式配置springmvc,会进入这个if。如果是注解方式则不进入
        try {
            //这里的this指向的DispatcherServlet,下面的代码是给DispatcherServlet中contextLocation属性赋值
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true); //赋值操作
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // Let subclasses do whatever initialization they like.
    initServletBean(); //子类实现
}

        基于xml方式配置springmvc,会进入if分支。PropertyValues是一个键值对,内容就是xml配置init-param标签,key=contextConfigLocation,value=classpath:springmvc04.xml。

        这段代码就是保存springmvc配置的,了解到这种程度即可。

三、FrameworkServlet#initServletBean

        这个方法大部分都是日志检查,最核心方法就是initWebApplicationContext,初始化IOC容器。initFrameworkServlet目前是空方法。

protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
		long startTime = System.currentTimeMillis();

		try {
            //初始化IOC容器 ApplicationContext
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet(); // 这方法是空
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (logger.isDebugEnabled()) {
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}

		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}
	protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
        //如果是注解方式的springmvc进入if分支,xml方式不进入
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext); //这里创建ioc容器
		}
        
        //spring异步事件,在创建ioc容器时会产生一个异步事件,去刷新ioc容器
        //这里简单起见,假设没有接收到事件,刷新操作会在当前线程完成
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

        //到这里只需要设置属性即可
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

这里有几点说明:

1)spring异步事件,是单独的,不去深究,这里假设ioc容器ApplicationContext创建之后,刷新操作可以看成是在当前线程中完成

2)attrName比较长,通过断点查看到其值是,org.springframework.web.servlet.FrameworkServlet.CONTEXT.DispatcherServlet,红色是我们在xml配置文件中指定servlet-name属性

四、DispatcherServlet#onRefresh

本质是初始化各种解析器


	/**
	 * 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);
	}

五、总结

        初始化流程相对清晰一些,当然并没有深入分析每个方法,只是把大体流程搞清楚了,下一篇介《SpringMvc源码分析-DispatchServlet处理流程》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值