主题

之前Spring相关的一些类,比如Enviromnent,BenFactory都接触了一些.有一些收获.但是直接看代码很多方法都不知为什么这样写.哪里会用到.因为太底层了.不好理解..现在从高层次的调用开始再研究下.应该会有新的理解.

所以从一个Web应用初始化开始学习.看看它经历了哪些步骤.做了哪些事情.

之前对spring的dispatcherServlet有一点点研究(http://www.cnblogs.com/abcwt112/p/5283674.html).

ContextLoaderListener

1个最普通的WEB项目如果要在servlet环境中用Spring.肯定是在web.xml里配置1个listener.这个linstener是1个入口,在内部肯定会创建Spring相关的applicationcontext并配置它.

  1. /*
  2. * Copyright 2002-2015 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. package org.springframework.web.context;
  18.  
  19. import javax.servlet.ServletContextEvent;
  20. import javax.servlet.ServletContextListener;
  21.  
  22. /**
  23. * Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
  24. * Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
  25. *
  26. * <p>This listener should be registered after {@link org.springframework.web.util.Log4jConfigListener}
  27. * in {@code web.xml}, if the latter is used.
  28. *
  29. * <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
  30. * application context via the {@link #ContextLoaderListener(WebApplicationContext)}
  31. * constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
  32. * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
  33. *
  34. * @author Juergen Hoeller
  35. * @author Chris Beams
  36. * @since 17.02.2003
  37. * @see org.springframework.web.WebApplicationInitializer
  38. * @see org.springframework.web.util.Log4jConfigListener
  39. */
  40. public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
  41.  
  42. /**
  43. * Create a new {@code ContextLoaderListener} that will create a web application
  44. * context based on the "contextClass" and "contextConfigLocation" servlet
  45. * context-params. See {@link ContextLoader} superclass documentation for details on
  46. * default values for each.
  47. * <p>This constructor is typically used when declaring {@code ContextLoaderListener}
  48. * as a {@code <listener>} within {@code web.xml}, where a no-arg constructor is
  49. * required.
  50. * <p>The created application context will be registered into the ServletContext under
  51. * the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
  52. * and the Spring application context will be closed when the {@link #contextDestroyed}
  53. * lifecycle method is invoked on this listener.
  54. * @see ContextLoader
  55. * @see #ContextLoaderListener(WebApplicationContext)
  56. * @see #contextInitialized(ServletContextEvent)
  57. * @see #contextDestroyed(ServletContextEvent)
  58. */
  59. public ContextLoaderListener() {
  60. }
  61.  
  62. /**
  63. * Create a new {@code ContextLoaderListener} with the given application context. This
  64. * constructor is useful in Servlet 3.0+ environments where instance-based
  65. * registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}
  66. * API.
  67. * <p>The context may or may not yet be {@linkplain
  68. * org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it
  69. * (a) is an implementation of {@link ConfigurableWebApplicationContext} and
  70. * (b) has <strong>not</strong> already been refreshed (the recommended approach),
  71. * then the following will occur:
  72. * <ul>
  73. * <li>If the given context has not already been assigned an {@linkplain
  74. * org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
  75. * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
  76. * the application context</li>
  77. * <li>{@link #customizeContext} will be called</li>
  78. * <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
  79. * specified through the "contextInitializerClasses" init-param will be applied.</li>
  80. * <li>{@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called</li>
  81. * </ul>
  82. * If the context has already been refreshed or does not implement
  83. * {@code ConfigurableWebApplicationContext}, none of the above will occur under the
  84. * assumption that the user has performed these actions (or not) per his or her
  85. * specific needs.
  86. * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
  87. * <p>In any case, the given application context will be registered into the
  88. * ServletContext under the attribute name {@link
  89. * WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
  90. * application context will be closed when the {@link #contextDestroyed} lifecycle
  91. * method is invoked on this listener.
  92. * @param context the application context to manage
  93. * @see #contextInitialized(ServletContextEvent)
  94. * @see #contextDestroyed(ServletContextEvent)
  95. */
  96. public ContextLoaderListener(WebApplicationContext context) {
  97. super(context);
  98. }
  99.  
  100. /**
  101. * Initialize the root web application context.
  102. */
  103. @Override
  104. public void contextInitialized(ServletContextEvent event) {
  105. initWebApplicationContext(event.getServletContext());
  106. }
  107.  
  108. /**
  109. * Close the root web application context.
  110. */
  111. @Override
  112. public void contextDestroyed(ServletContextEvent event) {
  113. closeWebApplicationContext(event.getServletContext());
  114. ContextCleanupListener.cleanupAttributes(event.getServletContext());
  115. }
  116.  
  117. }

既然是1个listener.spring相关步骤肯定写在listener的contextInitialized方法里.内部很简单的调用了父类的initWebApplicationContext方法,并传入了servletContext对象作为参数.看方法名就知道这个方法肯定是要初始化WebApplicationContext.

  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  2. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  3. throw new IllegalStateException(
  4. "Cannot initialize context because there is already a root application context present - " +
  5. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  6. }
  7.  
  8. Log logger = LogFactory.getLog(ContextLoader.class);
  9. servletContext.log("Initializing Spring root WebApplicationContext");
  10. if (logger.isInfoEnabled()) {
  11. logger.info("Root WebApplicationContext: initialization started");
  12. }
  13. long startTime = System.currentTimeMillis();
  14.  
  15. try {
  16. // Store context in local instance variable, to guarantee that
  17. // it is available on ServletContext shutdown.
  18. if (this.context == null) {
  19. this.context = createWebApplicationContext(servletContext);
  20. }
  21. if (this.context instanceof ConfigurableWebApplicationContext) {
  22. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  23. if (!cwac.isActive()) {
  24. // The context has not yet been refreshed -> provide services such as
  25. // setting the parent context, setting the application context id, etc
  26. if (cwac.getParent() == null) {
  27. // The context instance was injected without an explicit parent ->
  28. // determine parent for root web application context, if any.
  29. ApplicationContext parent = loadParentContext(servletContext);
  30. cwac.setParent(parent);
  31. }
  32. configureAndRefreshWebApplicationContext(cwac, servletContext);
  33. }
  34. }
  35. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  36.  
  37. ClassLoader ccl = Thread.currentThread().getContextClassLoader();
  38. if (ccl == ContextLoader.class.getClassLoader()) {
  39. currentContext = this.context;
  40. }
  41. else if (ccl != null) {
  42. currentContextPerThread.put(ccl, this.context);
  43. }
  44.  
  45. if (logger.isDebugEnabled()) {
  46. logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
  47. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
  48. }
  49. if (logger.isInfoEnabled()) {
  50. long elapsedTime = System.currentTimeMillis() - startTime;
  51. logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
  52. }
  53.  
  54. return this.context;
  55. }
  56. catch (RuntimeException ex) {
  57. logger.error("Context initialization failed", ex);
  58. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  59. throw ex;
  60. }
  61. catch (Error err) {
  62. logger.error("Context initialization failed", err);
  63. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
  64. throw err;
  65. }
  66. }

几个比较重要的步骤是:

1. 19行this.context = createWebApplicationContext(servletContext);

初始化1个WebApplicationContext,默认是XmlWebApplicationContext通过BeanUtils.instantiateClass创建的,XmlWebApplicationContext这个类名是写在org\springframework\web\context\ContextLoader.properties里的.

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

不喜欢的话自己也可以通过servletContext里去配置contextClass这个参数指定class来配置WebApplicationContext.但是我想一般没人这么做吧.

2. 32行configureAndRefreshWebApplicationContext(cwac, servletContext);

初始化wac.大概是最重要的入口了.后面再仔细看.

3. 35行servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

如果前面初始化成功的话就把wac绑定到servletContext的attr中(org.springframework.web.context.WebApplicationContext.ROOT),.所以其实我们在servlet环境中如果要获取wac,要么通过ApplicationContextAware要么通过这个servletContext的attr...都是可以的.

configureAndRefreshWebApplicationContext方法

  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
  2. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  3. // The application context id is still set to its original default value
  4. // -> assign a more useful id based on available information
  5. String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
  6. if (idParam != null) {
  7. wac.setId(idParam);
  8. } else {
  9. // Generate default id...
  10. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  11. ObjectUtils.getDisplayString(sc.getContextPath()));
  12. }
  13. }
  14.  
  15. wac.setServletContext(sc);
  16. String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
  17. if (configLocationParam != null) {
  18. wac.setConfigLocation(configLocationParam);
  19. }
  20.  
  21. // The wac environment's #initPropertySources will be called in any case when the context
  22. // is refreshed; do it eagerly here to ensure servlet property sources are in place for
  23. // use in any post-processing or initialization that occurs below prior to #refresh
  24. ConfigurableEnvironment env = wac.getEnvironment();
  25. if (env instanceof ConfigurableWebEnvironment) {
  26. // 一开始env里的propertySources里面的servletContextInitParams和servletContextConfigParams都是空的,需要用相应的servlet的值去替换
  27. ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
  28. }
  29.  
  30. customizeContext(sc, wac);
  31. wac.refresh();
  32. }

然后我们来看看这个最重要的初始化wac的方法.

L15 wac.setServletContext(sc); wac要初始化需要servlet环境相关的数据.

L16.获取servletContext里配置的contextConfigLocation的值.这个变量大家肯定不陌生.这个就是需要加载的spring配置文件的路径地址.

L18 contextConfigLocation值设置给wac.所以wac初始化需要spring的配置文件(废话).

L24-28 让wac的env去加载servlet环境相关的数据.因为之前wac是用beanUtils创建的.创建的时候并不知道你当前的环境有什么变量.所以这里需要加载一下servlet环境的properties.

initPropertySources有2个参数.第一个servletContext.第二个是servletConfig.很明显.这里是在listener而不是springMVC的dispatcherServlet里,所以这里servletConfig是null.

L30  customizeContext(sc, wac);在wac启动之前允许你定制一下.

  1. /**
  2. * Customize the {@link ConfigurableWebApplicationContext} created by this
  3. * ContextLoader after config locations have been supplied to the context
  4. * but before the context is <em>refreshed</em>.
  5. * <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
  6. * determines} what (if any) context initializer classes have been specified through
  7. * {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
  8. * {@linkplain ApplicationContextInitializer#initialize invokes each} with the
  9. * given web application context.
  10. * <p>Any {@code ApplicationContextInitializers} implementing
  11. * {@link org.springframework.core.Ordered Ordered} or marked with @{@link
  12. * org.springframework.core.annotation.Order Order} will be sorted appropriately.
  13. *
  14. * @param sc the current servlet context
  15. * @param wac the newly created application context
  16. * @see #CONTEXT_INITIALIZER_CLASSES_PARAM
  17. * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
  18. */
  19. protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
  20. // 找到 web.xml里配置的 globalInitializerClasses 和 contextInitializerClasses 对应的class
  21. List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
  22. determineContextInitializerClasses(sc);
  23. // 一般情况下没人配置,所以是empty.
  24. if (initializerClasses.isEmpty()) {
  25. // no ApplicationContextInitializers have been declared -> nothing to do
  26. return;
  27. }
  28.  
  29. // 如果这些ApplicationContextInitializer存在的话就调用他们的initialize方法.
  30. ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
  31. new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
  32.  
  33. for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
  34. Class<?> initializerContextClass =
  35. GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
  36. if (initializerContextClass != null) {
  37. Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
  38. "Could not add context initializer [%s] since its generic parameter [%s] " +
  39. "is not assignable from the type of application context used by this " +
  40. "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
  41. wac.getClass().getName()));
  42. }
  43. initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
  44. }
  45.  
  46. AnnotationAwareOrderComparator.sort(initializerInstances);
  47. for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
  48. initializer.initialize(wac);
  49. }
  50. }

可以参考我写的注释.就是允许你在servletContext中配置2个参数 globalInitializerClasses 和 contextInitializerClasses.他们对应的类都是ApplicationContextInitializer的实现.如果你配置了.那这里会调用

  1. initializer.initialize(wac);

这个方法执行你的回调函数.

我记得我第一家公司这里好像加载了一些properties文件..(虽然我现在想想不明白为什么要在这里加载而不是直接配置在spring的配置文件中....或者直接使用@PropertySource或者@ConfigurationProperties)..算是1个spring的扩展点吧.

L31 前面的配置也做完了..那就真正开始初始化wac了.执行wac.refresh();刷新wac.

小结

主要学习了XmlWebApplicationContext刷新前的ContextLoaderListener做的一些预备工作..比如:

1.默认加载的是哪个wac.你也可以自己定制.

2.env读取servletContext与Config的参数

3.自定义的customer,ApplicationContextInitializer

4.初始化wac成功以后绑定到servletContext的attr中

等等...

后续准备研究wac是怎么refresh的.

Spring 学习记录7 初识XmlWebApplicationContext的更多相关文章

  1. Spring 学习记录8 初识XmlWebApplicationContext(2)

    主题 接上文Spring 学习记录7 初识XmlWebApplicationContext refresh方法 refresh方法是定义在父类AbstractApplicationContext中的. ...

  2. 我的Spring学习记录(二)

    本篇就简单的说一下Bean的装配和AOP 本篇的项目是在上一篇我的Spring学习记录(一) 中项目的基础上进行开发的 1. 使用setter方法和构造方法装配Bean 1.1 前期准备 使用sett ...

  3. 我的Spring学习记录(四)

    虽然Spring管理这我们的Bean很方便,但是,我们需要使用xml配置大量的Bean信息,告诉Spring我们要干嘛,这还是挺烦的,毕竟当我们的Bean随之增多的话,xml的各种配置会让人很头疼. ...

  4. 我的Spring学习记录(五)

    在我的Spring学习记录(四)中使用了注解的方式对前面三篇做了总结.而这次,使用了用户登录及注册来对于本人前面四篇做一个应用案例,希望通过这个来对于我们的Spring的使用有一定的了解. 1. 程序 ...

  5. Spring 学习记录3 ConversionService

    ConversionService与Environment的关系 通过之前的学习(Spring 学习记录2 Environment),我已经Environment主要是负责解析properties和p ...

  6. Spring 学习记录6 BeanFactory(2)

    主题 除了Spring 学习记录5 BeanFactory 里写的几个接口外,BeanFactory的实现类还实现了一些其他接口,这篇文章主要介绍这些接口和实现类. 结构 DefaultListabl ...

  7. Spring学习记录(九)---通过工厂方法配置bean

    1. 使用静态工厂方法创建Bean,用到一个工厂类 例子:一个Car类,有brand和price属性. package com.guigu.spring.factory; public class C ...

  8. Spring学习记录(七)---表达式语言-SpEL

    SpEL---Spring Expression Language:是一个支持运行时查询和操作对象图表达式语言.使用#{...}作为定界符,为bean属性动态赋值提供了便利. ①对于普通的赋值,用Sp ...

  9. 【Android】学习记录<1> -- 初识ffmpeg

    工作需要用到ffmpeg来进行Android的软编码,对这玩意儿一点都不了解,做个学习记录先. FFmpeg:http://www.ffmpeg.org Fmpeg is the leading mu ...

随机推荐

  1. linux发行版本centos7.4上安装jdk,tomcat,mariadb良心教程

    准备工作: 本地安装:rpm -ivh 程序名 因为jdk,tomcat,mysql的安装过程需要从网上下载部分支持包才可以继续,所以要求提前安装下载好依赖. yum install glibc.i6 ...

  2. Linux下Eclipse配置安装 PyDev(Pydev插件一直不能成功,安装这个插件失败的问题)

    pydev插件安装方式如果采取从网络上下载,然后解压到eclipse中文件夹到方式,运行到时候可能会导致重启eclipse后根本看不到这个插件! 原因以及解决方式,看下面!  转自:http://ww ...

  3. RNG—随机数产生器

    RNG 随机数产生器 RNG g_rng(12345); /********************************************************************** ...

  4. WPF 竖排文字(转)

    ---恢复内容开始--- 想做一个WPF 文字竖排 类似上图.用在TabItem的header上面. <TextBlock FontSize="30" Text=" ...

  5. kong k8s 安装 以及可视化管理界面

    1. git  clone $ git clone git@github.com:Mashape/kong-dist-kubernetes.git $ cd kong-dist-kubernetes ...

  6. I/O复用——select和poll

    概述 I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写 ...

  7. [语法]C语言中二维数组做输入参数

    C语言中二维数组做输入参数时, 可以同时指定各维长度, 可以只指定第二维的长度, 不可以只指定第一维的长度, 不可以各维长度都不指定. 一句话总结:要指定至少指定第二维,都不指定是不行的. 具体栗子如 ...

  8. guaua学习,工具专题

    Preconditions 1,http://www.cnblogs.com/peida/p/Guava_Preconditions.html 1 .checkArgument(boolean) : ...

  9. bzoj 3158 千钧一发——网络流

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3158 发现偶数之间一定满足第二个条件:奇数之间一定满足第一个条件 ( \( (2m+1)^{ ...

  10. 完美版js金钱正则表达式校验

    <!doctype html> <html lang="en">  <head>   <meta charset="UTF-8& ...