`
wugaokai
  • 浏览: 62061 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring-MVC 源码分析(一):ContextLoaderListener 初始化

 
阅读更多

 

ContextLoaderListener这个监听器是启动根IoC容器并把它载入到Web容器的主要功能模块,也是整个Spring Web应用加载IoC的第一个地方。


为了方便在Web环境中使用IoC容器,Spring为Web应用提供了上下文的扩展接口WebApplicationContext来满足启动过程的需要,结构图如下:


 这个常量用于在ServletContext中存取根上下文
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

通过getServlectContext()方法可以得到当前Web容器的Servlet上下文环境,通过这个方法,相当于提供了一个Web容器级别的全局环境。
在启动过程中,Spring 会使用一个默认的WebApplicationContext实现作为IoC容器,就是XmlWebApplicationContext

XmlWebApplicationContext类的结构图


 
 /** 
      *   这里是设置默认BeanDefinition的地方,如果不特殊指定其他文件,IoC容器会从/WEB-INF/applicationContext.xml文件
      *   读取BeanDefinition来初始化IoC容器。
      * */
     public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

     /** Default prefix for building a config location for a namespace */
     public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

     /** Default suffix for building a config location for a namespace */
     public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

     /**
      * 这里是取得Resource位置的地方,使用默认的配置位/WEB-INF/applicationContext.xml。
      */
     @Override
     protected String[] getDefaultConfigLocations() {
          if (getNamespace() != null) {
              return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
          }
          else {
              return new String[] {DEFAULT_CONFIG_LOCATION};
          }
     }

     /**
      * 这个加载过程在容器refresh()时启动
      */
     @Override
     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
          // Create a new XmlBeanDefinitionReader for the given BeanFactory.
          // 对于XmlWebApplicationContext,当然是使用XmlBeanDefinitionReader来对BeanDefinition信息进行解析。
          XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
       
          // 因为XmlWebApplicationContext是DefaultResourceLoader的子类,
          // 所以这里同样使用DefaultResourceLoader来定位BeanDefinition。
          beanDefinitionReader.setResourceLoader(this);
          beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
       
          // 钩子,允许子类自定义XmlBeanDefinitionReader的属性配置
          initBeanDefinitionReader(beanDefinitionReader);
          //这里使用定义好的XmlBeanDefinitionReader来载入BeanDefinition
          loadBeanDefinitions(beanDefinitionReader);
     }
     
    
          /**
      * 如果有多个BeanDefinition文件的定义,需逐个载入,都是通过reader来完成的
      * 这个初始化过程是由refreshBeanFactory方法来完成的,这里只负责载入BeanDefinition。
      */
     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
          String[] configLocations = getConfigLocations();
          if (configLocations != null) {
              for (String configLocation : configLocations) {
                   reader.loadBeanDefinitions(configLocation);
              }
          }
     }
 


 
public interface ServletContextListener extends EventListener {
    //服务器启动的时候初始化入口    
    public void contextInitialized ( ServletContextEvent sce );
     //服务器销毁的时候销毁入口
    public void contextDestroyed ( ServletContextEvent sce );
}
 
ContextLoader 类是用来建立WEB环境的根上下文环境

第一步:
ContextLoaderListener 类的contextInitialized方法实现

 /**
      * Initialize the root web application context.
      */
     public void contextInitialized(ServletContextEvent event) {
           //这个方法实现的本意是提供一个占位符方法createContextLoader()给子类机会创建客户化的环境加载,
                //但是,后来这个证明不是非常有用的,已经鼓励不再使用了,事实上,子类可以通过重写本方法达到同样的目的  
          this.contextLoader = createContextLoader();
           //实际上是为了使用超类的默认实现           
          if (this.contextLoader == null) {
              this.contextLoader = this;
          }
           //调用超类的加载根共享Web应用程序环境的默认实现  
          this.contextLoader.initWebApplicationContext(event.getServletContext());
     }
 
第二步:
ContextLoader 类的 initWebApplicationContext(ServletContext servletContext)代码

 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
           //如果已经存在了根共享Web应用程序环境,则抛出异常提示客户  
          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");
          }
          //记录创建根Web应用程序环境的开始时间  
          long startTime = System.currentTimeMillis();

          try {
               //决定根Web应用程序环境是否存在父应用程序环境,一般是返回null
              ApplicationContext parent = loadParentContext(servletContext);

               //创建根Web应用程序环境,如果父环境存在则引用父环境,通常情况下父环境是不存在的 即parent=null
                    // 这是一个比较重要的方法,在第三步说明
              this.context = createWebApplicationContext(servletContext, parent);
               //把创建的根Web应用程序环境保存到Servlet环境中,每个派遣器Servlet加载的子环境会应用这个环境作为父环境  
               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) {
                      //如果线程和本类拥有不同的类加载器,则使用线程的类加载器作为关键在保存在一个映射对象里,保证析构时能拿到Web应用程序环境进行关闭操作  
                   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);
               //如果产生任何异常,则保存异常对象到Servlet环境里  
               servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
              throw ex;
          }
          catch (Error err) {
              logger.error("Context initialization failed", err);
               //如果产生任何错误,则保存错误对象到Servlet环境里  
               servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
              throw err;
          }
     }
 

第三步:
ContextLoader 类的 createWebApplicationContext(ServletContext, ApplicationContext) 代码

 protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
          //检测是否有配置的Web应用程序环境类(即存放bean的容器类),如果没有配置,则使用缺省的类XmlWebApplicationContext 
              // 例如在web.xml中配置 
              //  <context-param>
            //       <param-name>contextClass</param-name>
            //       <param-value>org.springframework.web.context.support.GenericWebApplicationContext</param-value>
              //  </context-param> 
          Class<?> contextClass = determineContextClass(sc);
          
          //如果配置的Web应用程序环境类不是可配置的Web应用程序环境的子类,则抛出异常,停止初始化
              //若按上面的配置,这里会抛出异常,因为不是ConfigurableWebApplicationContext的子类 
          if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
              throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                        "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
          }
           //实例化Web应用程序环境类 
          ConfigurableWebApplicationContext wac =
                   (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

          //设置Web应用程序环境的ID 
          if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
              //如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名 
              String servletContextName = sc.getServletContextName();
               wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(servletContextName));
          }
          else {
               // 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名  
              try {
                   String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
                   wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                             ObjectUtils.getDisplayString(contextPath));
              }
              catch (Exception ex) {
                   throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
              }
          }
          
           //如果父环境存在,则引用使用父环境  
          wac.setParent(parent);
          //保存Servlet环境  
          wac.setServletContext(sc);
          //设置根环境XML文件存放的位置
              //这里如果以下配置容器启动不会报错,但若不配置,则会去寻找/WEB-INF/applicationContext.xml,若该文件不存在就则抛出FileNotFound异常
              //<context-param>
              //     <param-name>contextConfigLocation</param-name>
              //     <param-value></param-value>
              //</context-param>  
          wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
          //提供子类可互换Web应用程序环境的机会 占位符方法
          customizeContext(sc, wac);          //刷新Web应用程序环境以加载Bean定义,这里才是把我们XML里定义的bean放入容器的时候
          wac.refresh();
          return wac;
     }
 

 

  • 大小: 30.6 KB
  • 大小: 47.8 KB
  • 大小: 17.8 KB
分享到:
评论

相关推荐

    java解决org.springframework.web.context.ContextLoaderListener

    java解决org.springframework.web.context.ContextLoaderListener

    Spring-5.1.5源码

    Spring framework 5.1.5源码 Main: spring-web/org.springframework.web.context.ContextLoaderListener

    springweb3.0MVC注解(附实例)

    web.xml 中定义了一个名为 annomvc 的 Spring MVC 模块,按照 Spring MVC 的契约,需要在 WEB-INF/annomvc-servlet.xml 配置文件中定义 Spring MVC 模块的具体配置。annomvc-servlet.xml 的配置内容如下所示: ...

    spring_MVC源码

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,...

    gradle-spring-4-mvc-boilerplate

    如该软件包所指定的那样,DelegatingFilterProxy被认为可与Spring Web MVC一起使用,并且仅与Spring Web MVC一起使用(aka控制器,带或不带注释)。 它似乎不能与普通的servlet-jsps一起使用,因为您似乎正在尝试...

    struts2-spring-plugin-2.1.2.jar

    struts2与spring的整合。导入struts2-spring-... spring监听器对应的API类为:org.springframework.web.context.ContextLoaderListener。 struts2-spring-plugin包为我们将struts2的对象工厂设置为spring的IoC容器,

    Spring MVC 入门实例

    如果你手上有一本《Spring in Action》, 那么你最好从第三部分"Spring 在 Web 层的应用--建立 Web 层"开始看, 否则那将是一场恶梦! 首先, 我需要在你心里建立起 Spring MVC 的基本概念. 基于 Spring 的 Web 应用...

    struts2驱动包

    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:49) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843) at org....

    spring-simple-web:使用 Spring Framework 的简单 Web (WAR) 项目

    Web 应用程序使用 Spring Web 侦听器初始化,例如web.xml org.springframework.web.context.ContextLoaderListener 。 Spring Web 侦听器使用web.xml的contextConfigLocation上下文参数进行初始化。 此设置的...

    信息: Deploying web application directory lx01

    严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener at ...

    Spring MVC 框架应用实例

    org.springframework.web.context.ContextLoaderListener &lt;/listener-class&gt; &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; &lt;filter-class&gt; org.springframework.web.filter.CharacterEncodingFilter...

    spring-web-2.5.jar

    org.springframework.web.context.ContextLoaderListener.class org.springframework.web.context.ContextLoaderServlet.class org.springframework.web.context.ServletConfigAware.class org.springframework.web....

    iLink:拉丁云代码测试Spring MVC

    参考(2)Eclipse和IDEA配置Maven2.Spring版本:5.0.73.MVC:Spring MVC配置(1)jar包:spring-webmvc.jar(2)web.xml配置SpringMVC监听类:org.springframework.web.context.ContextLoaderListener(3)SpringMVC核心配置...

    underdog-jersey-spring-example:Jersey Spring 与 Java 配置示例

    发生的情况是 Jersey 的SringWebInitializer注册了它自己的ContextLoaderListener并且我们尝试从我们的WebApplicationInitializer加载另一个的尝试导致抛出异常。 这让我很头疼。 然后我发现了这个,它有一些变通...

    Spring的监听器ContextLoaderListener的作用

    Spring的监听器ContextLoaderListener的作用

    简单spring MVC 配置

    &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt; &lt;servlet-name&gt;test&lt;/servlet-name&gt; &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet...

    wicket-spring-hibernate:一个快速入门库,用于开始使用 Apache Wicket、Spring 和 Hibernate

    它包含 ContextLoaderListener,它为您的所有组件以及 beans.xml 的位置创建一个 Spring 容器。 ##Use as a quickstart 使用这个项目的最好方法是作为 maven 原型(快速入门)。 这是如何做到的: 转到项目目录并...

    DOS命令使用方法(超全).

    &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt; &lt;!-- 利用spring监听 编码设置 --&gt; &lt;filter-name&gt;SpringCharacterEncodingFilter&lt;/filter-name&gt; &lt;filter-...

    Spring源码学习七:web应用自动装配Spring配置文件1

    在web应用启动读取web.xml时,发现配置了ContextLoaderListener,而ContextLoaderListener实现了ServletCo

    ssh整合时遇到常见错误 ContextLoaderListener not found 解决

    ssh整合时 被虐的经验之谈。内容虽然比较少,也是前人的工作经验。

Global site tag (gtag.js) - Google Analytics