以前Spring相關的一些類,好比Enviromnent,BenFactory都接觸了一些.有一些收穫.可是直接看代碼不少方法都不知爲何這樣寫.哪裏會用到.由於太底層了.很差理解..如今從高層次的調用開始再研究下.應該會有新的理解.html
因此從一個Web應用初始化開始學習.看看它經歷了哪些步驟.作了哪些事情.java
以前對spring的dispatcherServlet有一點點研究(http://www.cnblogs.com/abcwt112/p/5283674.html).web
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 }
既然是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...都是能夠的.
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的實現.若是你配置了.那這裏會調用
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的.