博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring实践---WebApplicationContext创建过程
阅读量:7024 次
发布时间:2019-06-28

本文共 15387 字,大约阅读时间需要 51 分钟。

hot3.png

ContextLoaderListener

Servlet容器实例化ContextLoaderListener

Servlet容器会实例化一个ContextLoaderListener

org.springframework.web.context.ContextLoaderListener

该类继承ContextLoader,实现了ServletContextListener接口,使之具有listener功能

public class ContextLoaderListener extends ContextLoader implements ServletContextListener

ContextLoaderListener 实现了ServletContextListener接口

public interface ServletContextListener extends EventListener {    /**     * web应用初始化开始的时候,收到通知, 所有的ServletContextListeners都会     * 收到通知,在任何filter或者servlet之前     */    public void contextInitialized(ServletContextEvent sce);    /**     * ServletContext准备关闭的时候,会调用该方法;     * 在contextDestroyed调用前,所有的servlet和filter都已经销毁了     */    public void contextDestroyed(ServletContextEvent sce);}
/**     * Initialize the root web application context.     */    public void contextInitialized(ServletContextEvent event) {        this.contextLoader = createContextLoader();        if (this.contextLoader == null) {            this.contextLoader = this;        }        this.contextLoader.initWebApplicationContext(event.getServletContext());    }    ...    /**     * Close the root web application context.     */    public void contextDestroyed(ServletContextEvent event) {        if (this.contextLoader != null) {            this.contextLoader.closeWebApplicationContext(event.getServletContext());        }        ContextCleanupListener.cleanupAttributes(event.getServletContext());    }
  1. ServletContextListener扩展了ContextLoader,使之有Listener功能
  2. public void contextInitialized(ServletContextEvent sce);方法中,获取ServletContext
  3. public void contextDestroyed(ServletContextEvent event);方法中,释放资源。

流程图描述该过程

Servlet容器加载实例化ContextLoaderListener调用contextInitialized(ServletContextEvent sce)End

contextInitialized方法内部实现

/**     * Initialize the root web application context.     */    public void contextInitialized(ServletContextEvent event) {    //createContextLoader方法已经弃用,返回永远是null        this.contextLoader = createContextLoader();        if (this.contextLoader == null) {            this.contextLoader = this;        }        //调用父类initWebApplicationContext方法,传入ServletContext        this.contextLoader.initWebApplicationContext(event.getServletContext());    }    /**     * Create the ContextLoader to use. Can be overridden in subclasses.     * @return the new ContextLoader     * @deprecated in favor of simply subclassing ContextLoaderListener itself     * (which extends ContextLoader, as of Spring 3.0)     */    @Deprecated    protected ContextLoader createContextLoader() {        return null;    }

关键代码: 

this.contextLoader.initWebApplicationContext(event.getServletContext());

得到信息: 

1. 主要初始化任务在initWebApplicationContext中实现.

ContextLoader

1. 初始化ApplicationContext — initWebApplicationContext

关键代码: 

1. this.context = createWebApplicationContext(servletContext); 
2. servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 
3. configureAndRefreshWebApplicationContext(cwac, servletContext);

得到信息: 

1. 该代码创建了WebApplicationContext 
2. 这个源代码很长(- -) 
3. 关键代码在createWebApplicationContext(servletContext);里面 
4. configureAndRefreshWebApplicationContext(cwac, servletContext);创建之后的配置工作,这个也很重要,以后再说,这里直说创建Context过程。

代码

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {            throw new IllegalStateException(                    "Cannot initialize context because there is already a root application context present - " +                    "check whether you have multiple ContextLoader* definitions in your web.xml!");        }        Log logger = LogFactory.getLog(ContextLoader.class);        servletContext.log("Initializing Spring root WebApplicationContext");        if (logger.isInfoEnabled()) {            logger.info("Root WebApplicationContext: initialization started");        }        long startTime = System.currentTimeMillis();        try {            // Store context in local instance variable, to guarantee that            // it is available on ServletContext shutdown.            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);            }            if (logger.isDebugEnabled()) {                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +                        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");            }            if (logger.isInfoEnabled()) {                long elapsedTime = System.currentTimeMillis() - startTime;                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");            }            return this.context;        }        catch (RuntimeException ex) {            logger.error("Context initialization failed", ex);            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);            throw ex;        }        catch (Error err) {            logger.error("Context initialization failed", err);            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);            throw err;        }    }

2. 创建ApplicationContext — createWebApplicationContext

关键代码: 

1. Class<?> contextClass = determineContextClass(sc); 
2. return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

得到信息: 

1. 根据传入的sevletContext,返回使用WebApplicationContext接口的哪一个实现类,默认是XmlWebApplicationContext 
2. 使用BeanUtils实例化该类

源代码

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {        Class
contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }

3. 决定使用哪个WebApplicationContext?—determineContextClass

关键代码: 

1. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 
2. public static final String CONTEXT_CLASS_PARAM = "contextClass"; 
3. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());

得到信息: 

1. 从Servlet InitParam中获取的contextClassName,决定到底使用WebApplicationContext接口的哪一个实现类。 
2. 如果你的web.xml中定义了如下片段,会使用你自己的WebApplicationContext (默认”contextClass”),否则就使用XmlWebApplicationContext 
3. 利用反射得到Class

web.xml

contextClass
Your ContextClass

代码

protected Class
determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }

总结

配置web.xml

org.springframework.web.context.ContextLoaderListener
  1. Servlet容器实例化ContextLoaderListener,并通知其调用initWebApplicationContext
  2. ContextLoaderListener调用createWebApplicationContext根据ServletContext创建出WebApplicationContext
  3. 调用configureAndRefreshWebApplicationContext进行接下来的操作

彻底晕了,后面的还是画个图吧

这里写图片描述

 

2:

  Spring IOC容器通过ServletContextListener对servlet容器的生命周期监听,从而实现了IOC的启动和销毁。

    注意:

1.分析框架代码时,要常使用类继承、调用关系等快捷键,可以更高效的学习,快捷键可以设置成你习惯的按键;

2.本文重在怎么自我分析框架代码,所以对其中解析需自己实际跟踪代码实践方可;

3.spring源代码版本 spring-framework-3.2.1.RELEASE。

 

 

预览

javax.servlet.ServletContext,Servlet容器接口。

javax.servlet.ServletContextListener,Servlet容器生命周期监听接口。

org.springframework.web.context.ContextLoaderListener,Spring IOC容器生命周期监听类。

org.springframework.web.context.ContextLoader,Spring IOC容器启动和销毁。

 

配置-监听ServletContext生命周期

在web.xml中spring配置了对ServletContext生命周期的监听,当Web容器启动和销毁时,触发Spring定义的IOC容器的启动和销毁,具体配置如下:

[html]  

 

  1. <listener>  
  2.         <listener-class>  
  3.             org.springframework.web.context.ContextLoaderListener  
  4.         </listener-class>  
  5.     </listener>  

 

入口-ContextLoaderListener对Spring IOC初始化和销毁

当web容器启动时会触发contextInitialized方法对spring ioc容器进行初始化,销毁时会触发contextDestroyed方法对spring ioc容器进行销毁,ServletContextListener接口如下:

 

  1. public void contextInitialized ( ServletContextEvent sce );  // ServletContext启动时触发  
  2.   
  3. public void contextDestroyed ( ServletContextEvent sce );    // ServletContext销毁时触发  

 

 

Spring IOC启动

spring ioc容器初始化具体代码如下:

  1. org.springframework.web.context.ContextLoaderListener  
  2.       
  3.     public void contextInitialized(ServletContextEvent event) {  
  4.         this.contextLoader = createContextLoader();  
  5.         if (this.contextLoader == null) {  
  6.             this.contextLoader = this;  
  7.         }  
  8.         // 对spring ioc容器进行初始化  
  9.         this.contextLoader.initWebApplicationContext(event.getServletContext());  
  10.     }  

 

IOC容器的初始化是由ContextLoader类执行:

  1. org.springframework.web.context.ContextLoader  
  2.       
  3.     public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
  4.         // 是否已经加载IOC容器  
  5.         if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
  6.             throw new IllegalStateException(  
  7.                     "Cannot initialize context because there is already a root application context present - " +  
  8.                     "check whether you have multiple ContextLoader* definitions in your web.xml!");  
  9.         }  
  10.   
  11.         Log logger = LogFactory.getLog(ContextLoader.class);  
  12.         servletContext.log("Initializing Spring root WebApplicationContext");  
  13.         if (logger.isInfoEnabled()) {  
  14.             logger.info("Root WebApplicationContext: initialization started");  
  15.         }  
  16.         long startTime = System.currentTimeMillis();  
  17.   
  18.         try {  
  19.             // Store context in local instance variable, to guarantee that  
  20.             // it is available on ServletContext shutdown.  
  21.             if (this.context == null) {  
  22.                 this.context = createWebApplicationContext(servletContext);  
  23.             }  
  24.             if (this.context instanceof ConfigurableWebApplicationContext) {  
  25.                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;  
  26.                 if (!cwac.isActive()) {  
  27.                     // The context has not yet been refreshed -> provide services such as  
  28.                     // setting the parent context, setting the application context id, etc  
  29.                     if (cwac.getParent() == null) {  
  30.                         // The context instance was injected without an explicit parent ->  
  31.                         // determine parent for root web application context, if any.  
  32.                         ApplicationContext parent = loadParentContext(servletContext);  
  33.                         cwac.setParent(parent);  
  34.                     }  
  35.                     // 此方法是刷新初始化IOC容器的地方  
  36.                     configureAndRefreshWebApplicationContext(cwac, servletContext);  
  37.                 }  
  38.             }  
  39.             // IOC容器加载后,将IOC容器保存于ServletContext容器中  
  40.             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
  41.   
  42.         ...  
  43.     }  

 

熟悉的refresh()方法的调用:

  1. org.springframework.web.context.ContextLoader    
  2.       
  3.     protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {  
  4.         if (ObjectUtils.identityToString(wac).equals(wac.getId())) {  
  5.             // The application context id is still set to its original default value  
  6.             // -> assign a more useful id based on available information  
  7.             String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);  
  8.             if (idParam != null) {  
  9.                 wac.setId(idParam);  
  10.             }  
  11.             else {  
  12.                 // Generate default id...  
  13.                 if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {  
  14.                     // Servlet <= 2.4: resort to name specified in web.xml, if any.  
  15.                     wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  16.                             ObjectUtils.getDisplayString(sc.getServletContextName()));  
  17.                 }  
  18.                 else {  
  19.                     wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  20.                             ObjectUtils.getDisplayString(sc.getContextPath()));  
  21.                 }  
  22.             }  
  23.         }  
  24.   
  25.         wac.setServletContext(sc);  
  26.         String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);  
  27.         if (initParameter != null) {  
  28.             wac.setConfigLocation(initParameter);  
  29.         }  
  30.         customizeContext(sc, wac);  
  31.           
  32.         // 熟悉的refresh()对Spring IOC容器进行加载,接下来的步骤就是 "自我分析-Spring IOC"文脏中的内容  
  33.         wac.refresh();  
  34.     }  

 

Spring IOC销毁

对Spring IOC容器和其他Spring环境信息进行销毁:

  1. org.springframework.web.context.ContextLoaderListener  
  2.       
  3.     public void contextDestroyed(ServletContextEvent event) {  
  4.         if (this.contextLoader != null) {  
  5.             this.contextLoader.closeWebApplicationContext(event.getServletContext());  
  6.         }  
  7.         ContextCleanupListener.cleanupAttributes(event.getServletContext());  
  8.     }  

具体清理销毁了spring的什么东西,自己再跟踪下代码即可。

 

 

 

spring的启动过程:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;

  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;

  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

说完了spring上下文的初始化过程,这三个上下文的关系应该就了解了。如还是不太清楚,我就爱莫能助了,只能自行看代码去了。

转载于:https://my.oschina.net/spinachgit/blog/1631268

你可能感兴趣的文章
21. Merge Two Sorted Lists
查看>>
SQL Server中date和datetime的区别
查看>>
HDu Battle
查看>>
python学习笔记Day4
查看>>
网站域名重定向
查看>>
Python全栈开发-有趣的小程序
查看>>
sdfsdsf
查看>>
最简单的实体手机测试移动端前端Vue Cli3搭建网站的方法
查看>>
AbsoluteDefense 项目Beta版本Postmortem结果
查看>>
数据结构和算法——将序列分解为单独的变量
查看>>
Kali Linux 安装教程-转
查看>>
【转360】KB4041678 Windows 仅安全更新(2017.10) 补丁更新后执行SQL出错! http://bbs.360.cn/thread-15201531-1-1.html...
查看>>
phantomjs + python 打造一个微信机器人
查看>>
[shell命令]文件查看/拼接:wc,sed,awk,cut,grep,paste,sz,cz
查看>>
【搜索】P1468 派对灯 Party Lamps
查看>>
C语言实现链表的原地置逆
查看>>
JavaScript关于闭包的理解和实例
查看>>
jquery-ui-widget
查看>>
VC Error spawning cl.exe
查看>>
IIS连接数据库:数据库连接出错,请检查连接字串
查看>>