SpringMVC源码(一)- 总览 和 ContextLoaderListener监听启动WebApplicationContext

目录

一、SpringMVC总览

二、ContextLoaderListener的contextInitialized监听事件

1、创建Web类型的ApplicationContext

2、执行refresh方法


    比如下web.xml配置文件的部分,方便下面的理解:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:Resource/SpringConf.xml</param-value>
</context-param>
  
  
<servlet>
    <servlet-name>springweb</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
	<init-param>  
		<param-name>contextConfigLocation</param-name>  
		<param-value>/WEB-INF/springweb.xml</param-value>  
	</init-param>  
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>springweb</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

一、SpringMVC总览

    SpringMVC启动时会启动WebApplicationContext类型的容器,并且会调用之前分析的refresh方法。还是从原始的SpringMVC开始,启动方式有三种,配置文件中配置ContextLoadServlet,ContextLoaderListener、ContextLoaderPlugin。不论哪种方式启动都大致相同。主要包括两部分:

1、ContextLoaderListener监听

    初始化Spring容器,WebApplicationContext类型,并且执行refresh方法

2、DispatcherServlet(一个特殊的Servlet,如上面的配置web.xml中进行配置了)

    1)、DispatcherServlet像普通Servlet一样,init中启动九大件为服务请求做准备

        HttpServletBean#init => DispatcherServlet#onRefresh => DispatcherServlet#initStrategies(初始化九大件)

    2)、当上面初始化完成后,则请求进入后拦截转发到DispatcherServlet

        DispatcherServlet#doService => DispatcherServlet#doDispatch(执行MVC流程)

 

二、ContextLoaderListenercontextInitialized监听事件

    当Web容器启动时,触发ContextLoaderListenercontextInitialized方法:

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    // 省略部分日志和try catch代码
    long startTime = System.currentTimeMillis();

    if (this.context == null) {
        this.context = createWebApplicationContext(servletContext);
    }
    if (this.context instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
        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 ->
                // determine parent for root web application context, if any.
                ApplicationContext parent = loadParentContext(servletContext);
                cwac.setParent(parent);
            }
            configureAndRefreshWebApplicationContext(cwac, servletContext);
        }
    }
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

    ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    if (ccl == ContextLoader.class.getClassLoader()) {
        currentContext = this.context;
    } else if (ccl != null) {
        currentContextPerThread.put(ccl, this.context);
    }

    return this.context;
}

    这里需要考虑容器是否其他地方已经创建了,但是不论是否创建。整个Spring MVC启动过程需要经历几个步骤:

1、createWebApplicationContext(创建Web类型的ApplicationContext)

2、configureAndRefreshWebApplicationContext(主要是refresh方法执行)

3、将初始化的WebApplicationContext容器,用key为ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE加载到ServletContext容器中

1、创建Web类型的ApplicationContext

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("省略");
    }
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

    先判断启动的WebApplicationContext的类型Class,再使用反射初始化。主要是看启动的Class是怎么确定的。

protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    if (contextClassName != null) {
        // 省略try catch代码
        return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
    }
    else {
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
        // 省略try catch代码
        return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
    }
}

    先看启动Web时是否有进行配置Class,否则就在默认的配置策略中进行获取。详细可以参见:策略模式-简单实现和Spring源码中的使用分析。从配置文件ContextLoader.properties中获取到的类型为(所以如果没有配置默认启动XmlWebApplicationContext,Spring Boot不会启动该类型,但是SpringMVC没有太大差异):

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

 

2、执行refresh方法

    不管怎么样都回执行configureAndRefreshWebApplicationContext方法,以启动Spring 容器,也就是之前一直分析refresh方法。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, 
                                                        ServletContext sc) {
    // 设置ConfigurableWebApplicationContext的容器Id
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        else {
            // Generate default id...
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }
    // 设置Web容器上下文,设置到Spring容器中
    wac.setServletContext(sc);
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    // 配置location,如果配置的话,则refresh方法时会调用Resource加载,解析,初始化
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    // 设置Environment,refresh时也分析过,如果没有则会从java.lang.System中
    // 获取数据初始化StandardEnvironment或其子类
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }
    // 初始化自定义的容器,如果自己配置的话(我们可以自定义实现ServletContextListener)
    customizeContext(sc, wac);
    // 调用refresh方法
    wac.refresh();
}

    1、设置WebApplicationContext的Id

    2、设置Web容器上下文,设置到Spring容器中

    3、配置location(比如上面配置中的contextConfigLocation,则refresh方法时会调用Resource加载,解析,初始化)

    4、初始化自定义的监听

    5、最重要的一步,启动Spring容器(可以从这里开始看:SpringIoc源码(五)- ApplicationContext(一)- 结构梳理

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值