学习学习容器初始化,若有不对的地方,请指出更正,大家共同学习学习。

此篇幅主要围绕着 ContextLoaderListener加载容器,理解其中的原理。

ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。

因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。

ContextLoaderListener启动的上下文为根上下文,DispatcherServlet所创建的上下文的的父上下文即为此根上下文,可在FrameworkServlet中的initWebApplicationContext中看出。

通常在web.xml中如下配置:

[html] view plain copy
  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>classpath*:server_spring.xml
  4. </param-value>
  5. </context-param>
  6. <listener>
  7. <listener-class>org.springframework.web.context.ContextLoaderListener
  8. </listener-class>
  9. </listener>

层次结构

ContextLoaderListener继承ContextLoader类实现ServletContextListener接口
其中它的主要功能是在ContextLoader中实现,ServletContextListener接口在package javax.servlet中以为servlet的api,
ServletContextListener又继承EventListener,此乃package java.util;中的接口了。
EventListener接口中无任何方法。
ServletContextListener中含有2方法,一个初始化一个销毁。
[java] view plain copy
  1. /**
  2. * Receives notification that the web application initialization
  3. * process is starting.
  4. *
  5. * <p>All ServletContextListeners are notified of context
  6. * initialization before any filters or servlets in the web
  7. * application are initialized.
  8. *
  9. * @param sce the ServletContextEvent containing the ServletContext
  10. * that is being initialized
  11. */
  12. public void contextInitialized(ServletContextEvent sce);
  13. /**
  14. * Receives notification that the ServletContext is about to be
  15. * shut down.
  16. *
  17. * <p>All servlets and filters will have been destroyed before any
  18. * ServletContextListeners are notified of context
  19. * destruction.
  20. *
  21. * @param sce the ServletContextEvent containing the ServletContext
  22. * that is being destroyed
  23. */
  24. public void contextDestroyed(ServletContextEvent sce);
ContextLoaderListener初始化容器时序图
在ContextLoaderListener中的contextInitialized方法
[java] view plain copy
  1. /**
  2. * Initialize the root web application context.
  3. */
  4. @Override
  5. public void contextInitialized(ServletContextEvent event) {
  6. initWebApplicationContext(event.getServletContext());
  7. }

初始化root跟web上下文,initWebApplicationContext方法在其父类ContextLoader中提供实现。

ContextLoader中initWebApplicationContext方法初始化根上下文
[java] view plain copy
  1. /**
  2. * Initialize Spring's web application context for the given servlet context,
  3. * using the application context provided at construction time, or creating a new one
  4. * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
  5. * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
  6. * @param servletContext current servlet context
  7. * @return the new WebApplicationContext
  8. * @see #ContextLoader(WebApplicationContext)
  9. * @see #CONTEXT_CLASS_PARAM
  10. * @see #CONFIG_LOCATION_PARAM
  11. */
  12. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  13. //这里判断是否在ServletContext中存在上下文,如果有,说明已载入过或配置文件出错,可以从错误信息中看出
  14. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != ) {
  15. throw new IllegalStateException(
  16. "Cannot initialize context because there is already a root application context present - " +
  17. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  18. }
  19. Log logger = LogFactory.getLog(ContextLoader.class);
  20. servletContext.log("Initializing Spring root WebApplicationContext");
  21. if (logger.isInfoEnabled()) {
  22. logger.info("Root WebApplicationContext: initialization started");
  23. }
  24. long startTime = System.currentTimeMillis();
  25. try {
  26. // Store context in local instance variable, to guarantee that
  27. // it is available on ServletContext shutdown.
  28. if (this.context == ) {
  29. this.context = createWebApplicationContext(servletContext);
  30. }
  31. if (this.context instanceof ConfigurableWebApplicationContext) {
  32. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  33. if (!cwac.isActive()) {
  34. // The context has not yet been refreshed -> provide services such as
  35. // setting the parent context, setting the application context id, etc
  36. if (cwac.getParent() == ) {
  37. // The context instance was injected without an explicit parent ->
  38. // determine parent for root web application context, if any.
  39. // 这里载入根上下文的父上下文
  40. ApplicationContext parent = loadParentContext(servletContext);
  41. cwac.setParent(parent);
  42. }
  43. //这里从web.xml中取得相关的初始化参数,对WebApplicationContext进行初始化
  44. configureAndRefreshWebApplicationContext(cwac, servletContext);
  45. }
  46. }
  47. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  48. ClassLoader ccl = Thread.currentThread().getContextClassLoader();
  49. if (ccl == ContextLoader.class.getClassLoader()) {
  50. currentContext = this.context;
  51. }
  52. else if (ccl != ) {
  53. currentContextPerThread.put(ccl, this.context);
  54. }
  55. if (logger.isDebugEnabled()) {
  56. logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
  57. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
  58. }
  59. if (logger.isInfoEnabled()) {
  60. long elapsedTime = System.currentTimeMillis() - startTime;
  61. logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
  62. }
  63. return this.context;
  64. }
  65. catch (RuntimeException ex) {
  66. logger.error("Context initialization failed", ex);
  67. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  68. throw ex;
  69. }
  70. catch (Error err) {
  71. logger.error("Context initialization failed", err);
  72. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
  73. throw err;
  74. }
  75. }

根据提供的servlet上下文去初始化Spring的web应用上下文,在构造时使用当前应用上下文或者在web.xml中配置参数contextClass和contextConfigLocation去创建新的上下文。

先判断是否在ServletContext中存在root上下文,如果有,说明已载入过或配置文件出错,可以从错误信息中看出。
通过createWebApplicationContext方法创建web应用上下文,此上下文必定是实现了ConfigurableWebApplicationContext接口,在设置parent for root web application context,在configureAndRefreshWebApplicationContext方法里构造bean工厂和容器里bean的创建,这里就不描述了,下次专门研究这块,最后将跟上下文存入servletContext里,同时根web应用上下文存入到currentContextPerThread,可供后续取出当前上下文,currentContextPerThread = new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);。
ContextLoader中createWebApplicationContext方法创建根上下文
[java] view plain copy
  1. /**
  2. * Instantiate the root WebApplicationContext for this loader, either the
  3. * default context class or a custom context class if specified.
  4. * <p>This implementation expects custom contexts to implement the
  5. * {@link ConfigurableWebApplicationContext} interface.
  6. * Can be overridden in subclasses.
  7. * <p>In addition, {@link #customizeContext} gets called prior to refreshing the
  8. * context, allowing subclasses to perform custom modifications to the context.
  9. * @param sc current servlet context
  10. * @return the root WebApplicationContext
  11. * @see ConfigurableWebApplicationContext
  12. */
  13. protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
  14. //这里需要确定我们载入的根WebApplication的类型,
  15. //由在web.xml中配置的contextClass中配置的参数, 如果没有使用默认的。
  16. Class<?> contextClass = determineContextClass(sc);
  17. //contextClass必须实现ConfigurableWebApplicationContext,否则抛异常
  18. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  19. throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
  20. "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
  21. }
  22. //初始化WebApplication,强转成ConfigurableWebApplicationContext
  23. return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  24. }

初始化根上下文,

最后返回值需强转成ConfigurableWebApplicationContext。
ContextLoader中determineContextClass方法找到根上下文的Class类型
[java] view plain copy
  1. /**
  2. * Return the WebApplicationContext implementation class to use, either the
  3. * default XmlWebApplicationContext or a custom context class if specified.
  4. * @param servletContext current servlet context
  5. * @return the WebApplicationContext implementation class to use
  6. * @see #CONTEXT_CLASS_PARAM
  7. * @see org.springframework.web.context.support.XmlWebApplicationContext
  8. */
  9. protected Class<?> determineContextClass(ServletContext servletContext) {
  10. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
  11. if (contextClassName != ) {
  12. try {
  13. return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
  14. }
  15. catch (ClassNotFoundException ex) {
  16. throw new ApplicationContextException(
  17. "Failed to load custom context class [" + contextClassName + "]", ex);
  18. }
  19. }
  20. else {
  21. contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
  22. try {
  23. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
  24. }
  25. catch (ClassNotFoundException ex) {
  26. throw new ApplicationContextException(
  27. "Failed to load default context class [" + contextClassName + "]", ex);
  28. }
  29. }
  30. }
Web.xml中配置了contextClass就取其值,但必须是实现ConfigurableWebApplicationContext,
没有的就取默认值XmlWebApplicationContext。
ContextClass默认值和ContextLoader.properties如下:
[java] view plain copy
  1. /**
  2. * Name of the class path resource (relative to the ContextLoader class)
  3. * that defines ContextLoader's default strategy names.
  4. */
  5. private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
  6. private static final Properties defaultStrategies;
  7. static {
  8. // Load default strategy implementations from properties file.
  9. // This is currently strictly internal and not meant to be customized
  10. // by application developers.
  11. try {
  12. ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
  13. defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
  14. }
  15. catch (IOException ex) {
  16. throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
  17. }
  18. }
[plain] view plain copy
  1. # Default WebApplicationContext implementation class for ContextLoader.
  2. # Used as fallback when no explicit context implementation has been specified as context-param.
  3. # Not meant to be customized by application developers.
  4. org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
其中loadParentContext
[java] view plain copy
  1. /**
  2. * Template method with default implementation (which may be overridden by a
  3. * subclass), to load or obtain an ApplicationContext instance which will be
  4. * used as the parent context of the root WebApplicationContext. If the
  5. * return value from the method is null, no parent context is set.
  6. * <p>The main reason to load a parent context here is to allow multiple root
  7. * web application contexts to all be children of a shared EAR context, or
  8. * alternately to also share the same parent context that is visible to
  9. * EJBs. For pure web applications, there is usually no need to worry about
  10. * having a parent context to the root web application context.
  11. * <p>The default implementation uses
  12. * {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
  13. * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
  14. * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
  15. * which will be shared by all other users of ContextsingletonBeanFactoryLocator
  16. * which also use the same configuration parameters.
  17. * @param servletContext current servlet context
  18. * @return the parent application context, or {@code null} if none
  19. * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
  20. */
  21. protected ApplicationContext loadParentContext(ServletContext servletContext) {
  22. ApplicationContext parentContext = ;
  23. String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
  24. String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
  25. if (parentContextKey != ) {
  26. // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"
  27. BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
  28. Log logger = LogFactory.getLog(ContextLoader.class);
  29. if (logger.isDebugEnabled()) {
  30. logger.debug("Getting parent context definition: using parent context key of '" +
  31. parentContextKey + "' with BeanFactoryLocator");
  32. }
  33. this.parentContextRef = locator.useBeanFactory(parentContextKey);
  34. parentContext = (ApplicationContext) this.parentContextRef.getFactory();
  35. }
  36. return parentContext;
  37. }

根据在web.xml中配置的locatorFactorySelector和parentContextKey来给根web应用上下设置父上下文,如果没配置的话,父上下文为空。

加载父上下文的主要原因是允许多重root web application contexts作为可共享的ERA context的子节点,或者对EJB可见的去交替共享同样的父上下文。For pure web applications, there is usually no need to worry about having a parent context to the root web application context。这句话明确告诉我们,对于纯粹的Web应用,通常不用担心root web application context的父上下文,也就是没有,为null。
在应用程序如何获取 WebApplicationContext 有多种方式,最简单的就是

1.WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

当前应用的WebApplicationContext就保存在 ContextLoader的currentContextPerThread属性当中

2.基于ServletContext上下文获取的方式

ServletContext sc = request.getSession().getServletContext();

ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);

ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(sc);

WebApplicationContext wac1 = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

3.还有一些更合适的,基于Spring提供的抽象类或者接口,在初始化Bean时注入ApplicationContext

3.1:继承自抽象类ApplicationObjectSupport

说明:抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。

Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。

3.2:继承自抽象类WebApplicationObjectSupport

说明:类似上面方法,调用getWebApplicationContext()获取WebApplicationContext

3.3:实现接口ApplicationContextAware

说明:实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。

总结:Context结构复杂,parentContext结构的作用,及如何的去加载bean工厂的逻辑原理。

SpringMVC容器初始化篇----ContextLoaderListener相关推荐

  1. SpringMVC源码分析_1 SpringMVC容器启动和加载原理

                                                                    SpringMVC源码分析_1 SpringMVC启动和加载原理     ...

  2. Spring IOC学习心得之源码级分析ContextLoaderListener的作用(IOC容器初始化入口)

    ContextLoaderListener类是负责初始化IOC容器,即在我们的web项目中,这里就是IOC容器初始化的入口,由这个类启动IOC容器的初始化. 它配置在web.xml中,比如如下配置: ...

  3. SpringMVC容器和Spring容器

    Tomcat&Jetty在启动时给每个Web应用创建一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring容器提供宿主环境. Tomcat&Jetty ...

  4. Web容器初始化过程

    一.SpringMVC启动过程 Spring的MVC是基于Servlet功能实现的,每个web工程中都有一个web.xml文件,web容器在启动的时候会加载这个配置文件,当一个web应用加载到web容 ...

  5. Spring容器 SpringMVC容器 web容器的关系

    说到spring和springmvc,其实有很多工作好多年的人也分不清他们有什么区别,如果你问他项目里用的什么MVC技术,他会说我们用的spring和mybatis,或者spring和hibernat ...

  6. Spring容器初始化和bean创建过程

    文章目录 Spring容器初始化过程(注解) 1.this() 初始化bean读取器和扫描器 2. register(annotatedClasses) 3 refresh()刷新上下文 前言:一直想 ...

  7. Spring容器和springmvc容器的区别联系

    Spring是根容器,SpringMVC是其子容器.子容器的创建依赖于父容器的创建.子容器(SpringMVC容器)可以访问父容器(Spring容器)的Bean,父容器(Spring容器)不能访问子容 ...

  8. AnnotationConfigApplicationContext容器初始化

    AnnotationConfigApplicationContext容器初始化目录 (Spring源码分析)AnnotationConfigApplicationContext容器初始化 this() ...

  9. Runc 容器初始化和容器逃逸

    更多奇技淫巧欢迎订阅博客:https://fuckcloudnative.io 前言 在每一个 Kubernetes 节点中,运行着 kubelet,负责为 Pod 创建销毁容器,kubelet 预定 ...

最新文章

  1. 在请求完成后回调delegate的方法。然而回调时经常遇到这种情况:delegate已经被释放...
  2. java 中文问号问题_解决java中的中文乱码问题(ZT)
  3. LaTeX 笔记:NFSS 那点事儿
  4. 译:9.使用Redis进行消息传递
  5. C++中指针和引用的选择
  6. indesign照片放入太大_猪肚鸡做法复杂,量太大,那来个简易版,用上大厨教的妙招更香了...
  7. java - 人员分配组合
  8. 你真的懂ArrayList吗?说说foreach与iterator时remove的区别
  9. linux virbr0是什么意思
  10. python3GUI--你喜欢的无损音乐下载工具(附源码)
  11. 第三章 图表辅助元素的定制
  12. 《运动改造大脑》总结
  13. 饥饿的小易(BFS问题)
  14. 如何处理团队的技术债务
  15. python -m详解
  16. 如何通俗理解 beta分布、汤普森采样和狄利克雷分布
  17. hyperf接入阿里云nacos配置中心
  18. 基于jQuery实现tab选项卡【js实现页签切换】
  19. ZJGSU OJ 2499 密码情书
  20. 内网渗透|内网攻防—多级代理

热门文章

  1. centos安装Flash插件
  2. 活用"端口碰撞技术"---远程管理的好方式
  3. 比特币核心(BCE)或许并没有你想象的强大
  4. 查询Sqlserver数据库死锁的一个存储过程
  5. XXL-REGISTRY v1.0.2 发布,分布式服务注册中心
  6. SpringBoot @Async Example
  7. 谷歌地图API位置请求_Google Maps API
  8. Oracle控制文件恢复
  9. Windows2008管理---第14章 高可用群集和QoS
  10. Android消息机制 Looper源码阅读