樂在其中

          以JEE為主攻,以Flex為點綴,以Eclipse RCP為樂趣
          請訪問http://www.inframesh.org

          首頁 新隨筆 聯系 管理
            43 Posts :: 0 Stories :: 8 Comments :: 0 Trackbacks

          Spring 和 Struts在web.xml中增加的配置:

             1: <!-- spring的配置 -->
             2: <context-param>
             3:     <param-name>contextConfigLocation</param-name>
             4:     <param-value>classpath:/SpringContext/applicationContext-web.xml</param-value>
             5: </context-param>    
             7:  
             8: <listener>
             9:     <listener-class>org.springframework.web.context.ContextLoaderListener
            10:     </listener-class>
            11: </listener>
            12:  
            13: <filter>
            14:     <filter-name>struts2</filter-name>
            15:     <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
            17:     <init-param>
            18:         <param-name>config</param-name>
            19:         <param-value>struts-default.xml,struts-plugin.xml,struts/struts.xml</param-value>
            20:     </init-param>
            21: </filter>
            22:  
            23: <filter-mapping>
            24:     <filter-name>struts2</filter-name>
            25:     <url-pattern>*.do</url-pattern>
            26: </filter-mapping>

           

          第一個tag定義的是spring的配置文件地址到環境參數(context parameter)

          第二個tag定義一個listener為org.springframework.web.context.ContextLoaderListener,這里相當于j2ee容器給我們提供的main函數的切入點,可以讓我們做一些系統初始化的工作,需要實現的類是:javax.servlet.ServletContextListener

          第三個tag則定義了struts2的一個filter。Filter則是對每次請求(可以通過filter-mapping指定)做過濾處理,請求首先請過filter鏈的處理,然后再到HttpServlet的init方法。對應的類是:javax.servlet.Filter。上面先配置了一個filter,對應的類是org.apache.struts2.dispatcher.FilterDispatcher,參數則是struts的配置文件位置

          第四個參數定義了filter怎樣行為,顯然它對.do為后綴的請求應用struts2這個名稱的filter

          這里需要首先搞清楚servlet規范中什么是listener?
           
          詳細請參見 Servlet Listener和Filter

           

          查看ContextLoaderListener可知,它正好繼承了javax.servlet.ServletContextListener,用于監聽javax.servlet.ServletContextEvent事件

          /*
           * Copyright 2002-2007 the original author or authors.
           *
           * Licensed under the Apache License, Version 2.0 (the "License");
           * you may not use this file except in compliance with the License.
           * You may obtain a copy of the License at
           *
           *      http://www.apache.org/licenses/LICENSE-2.0
           *
           * Unless required by applicable law or agreed to in writing, software
           * distributed under the License is distributed on an "AS IS" BASIS,
           * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           * See the License for the specific language governing permissions and
           * limitations under the License.
           */
           
          package org.springframework.web.context;
           
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
           
          /**
           * Bootstrap listener to start up Spring's root {@link WebApplicationContext}.
           * Simply delegates to {@link ContextLoader}.
           *
           * <p>This listener should be registered after
           * {@link org.springframework.web.util.Log4jConfigListener}
           * in <code>web.xml</code>, if the latter is used.
           *
           * @author Juergen Hoeller
           * @since 17.02.2003
           * @see ContextLoaderServlet
           * @see org.springframework.web.util.Log4jConfigListener
           */
          public class ContextLoaderListener implements ServletContextListener {
           
              private ContextLoader contextLoader;
           
           
              /**
               * Initialize the root web application context.
               */
              public void contextInitialized(ServletContextEvent event) {
                  this.contextLoader = createContextLoader();
                  this.contextLoader.initWebApplicationContext(event.getServletContext());
              }
           
              /**
               * Create the ContextLoader to use. Can be overridden in subclasses.
               * @return the new ContextLoader
               */
              protected ContextLoader createContextLoader() {
                  return new ContextLoader();
              }
           
              /**
               * Return the ContextLoader used by this listener.
               * @return the current ContextLoader
               */
              public ContextLoader getContextLoader() {
                  return this.contextLoader;
              }
           
           
              /**
               * Close the root web application context.
               */
              public void contextDestroyed(ServletContextEvent event) {
                  if (this.contextLoader != null) {
                      this.contextLoader.closeWebApplicationContext(event.getServletContext());
                  }
              }
           
          }

          此類implement了ServletContextListener的兩個方法:

          public void contextInitialized(ServletContextEvent event);
          public void contextDestroyed(ServletContextEvent event);

          分別做context的初始化和銷毀

          另外提供了一個protected方法:protected ContextLoader createContextLoader()  用于創建真正做事情的代理類CotextLoader

          和一個public方法:public ContextLoader getContextLoader();

           

          可見,這個Listener類直接將工作代理給了ContextLoader類了

           

          ____________________________________________________________________________________________________________________________

          我們按圖索驥,下面再分析org.springframework.web.context.ContextLoader

          ContextLoader里面有個私有成員:private WebApplicationContext context

          此變量提供了create方法:

          createWebApplicationContext(ServletContext servletContext, ApplicationContext parent)

          因為Spring提供了多種WebApplicationContext類,所以需要一個方法來決定使用哪個WebApplicationContextContext類

          protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
                  String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
                  if (contextClassName != null) {
                      try {
                          return ClassUtils.forName(contextClassName);
                      }
                      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);
                      }
                  }
              }

           

          public static final String CONTEXT_CLASS_PARAM = "contextClass";

          所以如果在web.xml中的<context-param> </context-param>中定義了參數contextClass,那么直接就決定了用此Context類

          否則,就應用缺省策略來決定使用哪個Context類。

          缺省策略如下:

          private static final Properties defaultStrategies;
           
          static {
              // Load default strategy implementations from properties file.
              // This is currently strictly internal and not meant to be customized
              // by application developers.
              try {
                  ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
                  defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
              }
              catch (IOException ex) {
                  throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
              }
          }

          實際上,缺省策略從org\springframework\web\context\ContextLoader.properties 文件中取得屬性org.springframework.web.context.WebApplicationContext

          我們看看ContextLoader.properties文件的內容:

          # Default WebApplicationContext implementation class for ContextLoader.
          # Used as fallback when no explicit context implementation has been specified as context-param.
          # Not meant to be customized by application developers.
           
          org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

          顯然,缺省的Context類就是 XmlWebApplicationContext類

          !!!真累啊,Spring...

           

          OK,總算知道用哪個Context類了,那么現在到了create這個Context實例的時候了

          protected WebApplicationContext createWebApplicationContext(
                      ServletContext servletContext, ApplicationContext parent) throws BeansException {
           
                  Class contextClass = determineContextClass(servletContext);
                  if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
                      throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                              "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
                  }
           
                  ConfigurableWebApplicationContext wac =
                          (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
                  wac.setParent(parent);
                  wac.setServletContext(servletContext);
                  wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
                  customizeContext(servletContext, wac);
                  wac.refresh();
           
                  return wac;
              }

          這里很容易理解,只不過它的創建不是直接new,而是封裝了一層,調用BeanUtils.instantiateClass()工具方法

          接下來設定WebApplicationCcontext實例的parent, servletContext,

          其中配置文件位置(web.xml中)的contextConfigLocation參數指定的

          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:/SpringContext/applicationContext-web.xml
              </param-value>
          </context-param>

          倒數第二行調用的customizeContext()方法目前Spring實現代碼是空的,估計留作以后(看來萬事要留余地啊^_^)

          最后一件事就是調用WebApplicationContext的refresh()方法。(這個方法是個stratup方法,很重要。他干的事情后面會著重涉及)

           

          最后就是真正做事的initWebApplicationContext()方法:

          public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
                      throws IllegalStateException, BeansException {
           
                  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!");
                  }
           
                  servletContext.log("Initializing Spring root WebApplicationContext");
                  if (logger.isInfoEnabled()) {
                      logger.info("Root WebApplicationContext: initialization started");
                  }
                  long startTime = System.currentTimeMillis();
           
                  try {
                      // Determine parent for root web application context, if any.
                      ApplicationContext parent = loadParentContext(servletContext);
           
                      // Store context in local instance variable, to guarantee that
                      // it is available on ServletContext shutdown.
                      this.context = createWebApplicationContext(servletContext, parent);
                      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                      currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), 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;
                  }
              }

          它其實做得事情很簡單,調用loadParentContext()方法獲取父context,調用createWebApplicationContext()創建WebApplicationContext實例

           

          一切都似乎完成了,至少我們明白了配置文件是如何加載進去的,至于IoC容器如何幫你注入配置文件中的bean,下次再探索。線索是什么呢?你還記得在createWebApplication()的最后一步做什么了嗎?refresh(), 對了,這個就是你作為職業"嘿客"要探索的下一個線索

          posted on 2008-12-26 16:53 suprasoft Inc,. 閱讀(2636) 評論(1)  編輯  收藏 所屬分類: J2EE

          Feedback

          # re: 讀Spring源代碼之按圖索驥(一)Context創建與配置文件加載 2013-10-30 21:08 馬小龍
          真的很不錯  回復  更多評論
            

          ©2005-2008 Suprasoft Inc., All right reserved.
          主站蜘蛛池模板: 南华县| 阜平县| 高邮市| 凉城县| 泌阳县| 汕头市| 乐至县| 汤阴县| 石狮市| 周至县| 上思县| 合江县| 通河县| 阜新| 望江县| 绥中县| 百色市| 凉山| 峡江县| 墨玉县| 乾安县| 南川市| 镇巴县| 洮南市| 徐闻县| 雷波县| 崇信县| 蓝田县| 曲周县| 高清| 平陆县| 元朗区| 桃园市| 神农架林区| 东城区| 尖扎县| 垦利县| 普兰店市| 尚义县| 女性| 娄底市|