Spring中ContextLoaderListener监听详解

 更新时间:2024年01月05日 08:59:48   作者:it_lihongmin  
这篇文章主要介绍了Spring中ContextLoaderListener监听详解,SpringMVC启动时会启动WebApplicationContext类型的容器,并且会调用之前分析的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流程)

二、ContextLoaderListener的contextInitialized监听事件

当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容器中

创建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

执行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(一)- 结构梳理)

到此这篇关于Spring中ContextLoaderListener监听详解的文章就介绍到这了,更多相关ContextLoaderListener监听内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决tk.mybatis中写自定义的mapper的问题

    解决tk.mybatis中写自定义的mapper的问题

    这篇文章主要介绍了使用tk.mybatis中写自定义的mapper的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java实现求解一元n次多项式的方法示例

    Java实现求解一元n次多项式的方法示例

    这篇文章主要介绍了Java实现求解一元n次多项式的方法,涉及java高斯消元法处理矩阵运算解多项式的相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • SpringBoot使用minio进行文件管理的流程步骤

    SpringBoot使用minio进行文件管理的流程步骤

    MinIO 是一个高性能的对象存储系统,兼容 Amazon S3 API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文给大家介绍了SpringBoot使用minio进行文件管理的流程步骤,需要的朋友可以参考下
    2025-01-01
  • java 数值类型分秒时间格式化的实例代码

    java 数值类型分秒时间格式化的实例代码

    这篇文章主要介绍了java 数值类型分秒时间格式化的实例代码的相关资料,将秒或分钟的值转换为xx天xx小时xx分钟xx秒 如果 “xx” 为0 自动缺省,需要的朋友可以参考下
    2017-07-07
  • SpringBoot 使用 FTP 操作文件的过程(删除、上传、下载文件)

    SpringBoot 使用 FTP 操作文件的过程(删除、上传、下载文件)

    这篇文章主要介绍了SpringBoot 使用 FTP 操作文件,主要包括配置ftp服务器,上传、删除、下载文件操作,本文结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • SQL返回Map集合或者对象的操作

    SQL返回Map集合或者对象的操作

    这篇文章主要介绍了SQL返回Map集合或者对象的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java数组基础详解

    java数组基础详解

    下面小编就为大家带来一篇Java创建数组的几种方式总结。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望能给大家带来帮助
    2021-06-06
  • IDEA最新激活码2021(IDEA2020.3.2最新永久激活方法)

    IDEA最新激活码2021(IDEA2020.3.2最新永久激活方法)

    这篇文章主要介绍了IDEA最新激活码2021(IDEA2020.3.2最新永久激活方法),本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Java不可变类机制浅析

    Java不可变类机制浅析

    所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:Interger、Long和String等。接下来通过本文给大家介绍Java不可变类机制,需要的朋友参考下
    2017-02-02
  • Java异常处理与throws关键字用法分析

    Java异常处理与throws关键字用法分析

    这篇文章主要介绍了Java异常处理与throws关键字用法,结合实例形式分析了java常见的异常、错误处理及throws关键字相关使用技巧、注意事项,需要的朋友可以参考下
    2019-01-01

最新评论