Spring 學習記錄7 初識XmlWebApplicationContext

主題

以前Spring相關的一些類,好比Enviromnent,BenFactory都接觸了一些.有一些收穫.可是直接看代碼不少方法都不知爲何這樣寫.哪裏會用到.由於太底層了.很差理解..如今從高層次的調用開始再研究下.應該會有新的理解.html

因此從一個Web應用初始化開始學習.看看它經歷了哪些步驟.作了哪些事情.java

以前對spring的dispatcherServlet有一點點研究(http://www.cnblogs.com/abcwt112/p/5283674.html).web

 

 

ContextLoaderListener

1個最普通的WEB項目若是要在servlet環境中用Spring.確定是在web.xml裏配置1個listener.這個linstener是1個入口,在內部確定會建立Spring相關的applicationcontext並配置它.spring

  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     /**
102      * Initialize the root web application context.
103      */
104     @Override
105     public void contextInitialized(ServletContextEvent event) {
106         initWebApplicationContext(event.getServletContext());
107     }
108 
109 
110     /**
111      * Close the root web application context.
112      */
113     @Override
114     public void contextDestroyed(ServletContextEvent event) {
115         closeWebApplicationContext(event.getServletContext());
116         ContextCleanupListener.cleanupAttributes(event.getServletContext());
117     }
118 
119 }
View Code

既然是1個listener.spring相關步驟確定寫在listener的contextInitialized方法裏.內部很簡單的調用了父類的initWebApplicationContext方法,並傳入了servletContext對象做爲參數.看方法名就知道這個方法確定是要初始化WebApplicationContext.express

 

 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     }

幾個比較重要的步驟是:apache

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

初始化1個WebApplicationContext,默認是XmlWebApplicationContext經過BeanUtils.instantiateClass建立的,XmlWebApplicationContext這個類名是寫在org\springframework\web\context\ContextLoader.properties裏的.less

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

不喜歡的話本身也能夠經過servletContext裏去配置contextClass這個參數指定class來配置WebApplicationContext.可是我想通常沒人這麼作吧.ide

 

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     }
View Code

能夠參考我寫的註釋.就是容許你在servletContext中配置2個參數 globalInitializerClasses 和 contextInitializerClasses.他們對應的類都是ApplicationContextInitializer的實現.若是你配置了.那這裏會調用

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的.

相關文章
相關標籤/搜索