001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.context; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Map; 023import java.util.Properties; 024import java.util.concurrent.ConcurrentHashMap; 025import javax.servlet.ServletContext; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029 030import org.springframework.beans.BeanUtils; 031import org.springframework.beans.factory.access.BeanFactoryLocator; 032import org.springframework.beans.factory.access.BeanFactoryReference; 033import org.springframework.context.ApplicationContext; 034import org.springframework.context.ApplicationContextException; 035import org.springframework.context.ApplicationContextInitializer; 036import org.springframework.context.ConfigurableApplicationContext; 037import org.springframework.context.access.ContextSingletonBeanFactoryLocator; 038import org.springframework.core.GenericTypeResolver; 039import org.springframework.core.annotation.AnnotationAwareOrderComparator; 040import org.springframework.core.env.ConfigurableEnvironment; 041import org.springframework.core.io.ClassPathResource; 042import org.springframework.core.io.support.PropertiesLoaderUtils; 043import org.springframework.util.ClassUtils; 044import org.springframework.util.ObjectUtils; 045import org.springframework.util.StringUtils; 046 047/** 048 * Performs the actual initialization work for the root application context. 049 * Called by {@link ContextLoaderListener}. 050 * 051 * <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter at the 052 * {@code web.xml} context-param level to specify the context class type, falling 053 * back to {@link org.springframework.web.context.support.XmlWebApplicationContext} 054 * if not found. With the default ContextLoader implementation, any context class 055 * specified needs to implement the {@link ConfigurableWebApplicationContext} interface. 056 * 057 * <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} context-param 058 * and passes its value to the context instance, parsing it into potentially multiple 059 * file paths which can be separated by any number of commas and spaces, e.g. 060 * "WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml". 061 * Ant-style path patterns are supported as well, e.g. 062 * "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/**/*Context.xml". 063 * If not explicitly specified, the context implementation is supposed to use a 064 * default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml"). 065 * 066 * <p>Note: In case of multiple config locations, later bean definitions will 067 * override ones defined in previously loaded files, at least when using one of 068 * Spring's default ApplicationContext implementations. This can be leveraged 069 * to deliberately override certain bean definitions via an extra XML file. 070 * 071 * <p>Above and beyond loading the root application context, this class can optionally 072 * load or obtain and hook up a shared parent context to the root application context. 073 * See the {@link #loadParentContext(ServletContext)} method for more information. 074 * 075 * <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web 076 * application context via the {@link #ContextLoader(WebApplicationContext)} 077 * constructor, allowing for programmatic configuration in Servlet 3.0+ environments. 078 * See {@link org.springframework.web.WebApplicationInitializer} for usage examples. 079 * 080 * @author Juergen Hoeller 081 * @author Colin Sampaleanu 082 * @author Sam Brannen 083 * @since 17.02.2003 084 * @see ContextLoaderListener 085 * @see ConfigurableWebApplicationContext 086 * @see org.springframework.web.context.support.XmlWebApplicationContext 087 */ 088public class ContextLoader { 089 090 /** 091 * Config param for the root WebApplicationContext id, 092 * to be used as serialization id for the underlying BeanFactory: {@value} 093 */ 094 public static final String CONTEXT_ID_PARAM = "contextId"; 095 096 /** 097 * Name of servlet context parameter (i.e., {@value}) that can specify the 098 * config location for the root context, falling back to the implementation's 099 * default otherwise. 100 * @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION 101 */ 102 public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; 103 104 /** 105 * Config param for the root WebApplicationContext implementation class to use: {@value} 106 * @see #determineContextClass(ServletContext) 107 */ 108 public static final String CONTEXT_CLASS_PARAM = "contextClass"; 109 110 /** 111 * Config param for {@link ApplicationContextInitializer} classes to use 112 * for initializing the root web application context: {@value} 113 * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext) 114 */ 115 public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses"; 116 117 /** 118 * Config param for global {@link ApplicationContextInitializer} classes to use 119 * for initializing all web application contexts in the current application: {@value} 120 * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext) 121 */ 122 public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses"; 123 124 /** 125 * Optional servlet context parameter (i.e., "{@code locatorFactorySelector}") 126 * used only when obtaining a parent context using the default implementation 127 * of {@link #loadParentContext(ServletContext servletContext)}. 128 * Specifies the 'selector' used in the 129 * {@link ContextSingletonBeanFactoryLocator#getInstance(String selector)} 130 * method call, which is used to obtain the BeanFactoryLocator instance from 131 * which the parent context is obtained. 132 * <p>The default is {@code classpath*:beanRefContext.xml}, 133 * matching the default applied for the 134 * {@link ContextSingletonBeanFactoryLocator#getInstance()} method. 135 * Supplying the "parentContextKey" parameter is sufficient in this case. 136 */ 137 public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector"; 138 139 /** 140 * Optional servlet context parameter (i.e., "{@code parentContextKey}") 141 * used only when obtaining a parent context using the default implementation 142 * of {@link #loadParentContext(ServletContext servletContext)}. 143 * Specifies the 'factoryKey' used in the 144 * {@link BeanFactoryLocator#useBeanFactory(String factoryKey)} method call, 145 * obtaining the parent application context from the BeanFactoryLocator instance. 146 * <p>Supplying this "parentContextKey" parameter is sufficient when relying 147 * on the default {@code classpath*:beanRefContext.xml} selector for 148 * candidate factory references. 149 */ 150 public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; 151 152 /** 153 * Any number of these characters are considered delimiters between 154 * multiple values in a single init-param String value. 155 */ 156 private static final String INIT_PARAM_DELIMITERS = ",; \t\n"; 157 158 /** 159 * Name of the class path resource (relative to the ContextLoader class) 160 * that defines ContextLoader's default strategy names. 161 */ 162 private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; 163 164 165 private static final Properties defaultStrategies; 166 167 static { 168 // Load default strategy implementations from properties file. 169 // This is currently strictly internal and not meant to be customized 170 // by application developers. 171 try { 172 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); 173 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 174 } 175 catch (IOException ex) { 176 throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); 177 } 178 } 179 180 181 /** 182 * Map from (thread context) ClassLoader to corresponding 'current' WebApplicationContext. 183 */ 184 private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread = 185 new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1); 186 187 /** 188 * The 'current' WebApplicationContext, if the ContextLoader class is 189 * deployed in the web app ClassLoader itself. 190 */ 191 private static volatile WebApplicationContext currentContext; 192 193 194 /** 195 * The root WebApplicationContext instance that this loader manages. 196 */ 197 private WebApplicationContext context; 198 199 /** 200 * Holds BeanFactoryReference when loading parent factory via 201 * ContextSingletonBeanFactoryLocator. 202 */ 203 private BeanFactoryReference parentContextRef; 204 205 /** Actual ApplicationContextInitializer instances to apply to the context */ 206 private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers = 207 new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>(); 208 209 210 /** 211 * Create a new {@code ContextLoader} that will create a web application context 212 * based on the "contextClass" and "contextConfigLocation" servlet context-params. 213 * See class-level documentation for details on default values for each. 214 * <p>This constructor is typically used when declaring the {@code 215 * ContextLoaderListener} subclass as a {@code <listener>} within {@code web.xml}, as 216 * a no-arg constructor is required. 217 * <p>The created application context will be registered into the ServletContext under 218 * the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} 219 * and subclasses are free to call the {@link #closeWebApplicationContext} method on 220 * container shutdown to close the application context. 221 * @see #ContextLoader(WebApplicationContext) 222 * @see #initWebApplicationContext(ServletContext) 223 * @see #closeWebApplicationContext(ServletContext) 224 */ 225 public ContextLoader() { 226 } 227 228 /** 229 * Create a new {@code ContextLoader} with the given application context. This 230 * constructor is useful in Servlet 3.0+ environments where instance-based 231 * registration of listeners is possible through the {@link ServletContext#addListener} 232 * API. 233 * <p>The context may or may not yet be {@linkplain 234 * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation 235 * of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong> 236 * already been refreshed (the recommended approach), then the following will occur: 237 * <ul> 238 * <li>If the given context has not already been assigned an {@linkplain 239 * ConfigurableApplicationContext#setId id}, one will be assigned to it</li> 240 * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to 241 * the application context</li> 242 * <li>{@link #customizeContext} will be called</li> 243 * <li>Any {@link ApplicationContextInitializer}s specified through the 244 * "contextInitializerClasses" init-param will be applied.</li> 245 * <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li> 246 * </ul> 247 * If the context has already been refreshed or does not implement 248 * {@code ConfigurableWebApplicationContext}, none of the above will occur under the 249 * assumption that the user has performed these actions (or not) per his or her 250 * specific needs. 251 * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples. 252 * <p>In any case, the given application context will be registered into the 253 * ServletContext under the attribute name {@link 254 * WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and subclasses are 255 * free to call the {@link #closeWebApplicationContext} method on container shutdown 256 * to close the application context. 257 * @param context the application context to manage 258 * @see #initWebApplicationContext(ServletContext) 259 * @see #closeWebApplicationContext(ServletContext) 260 */ 261 public ContextLoader(WebApplicationContext context) { 262 this.context = context; 263 } 264 265 266 /** 267 * Specify which {@link ApplicationContextInitializer} instances should be used 268 * to initialize the application context used by this {@code ContextLoader}. 269 * @since 4.2 270 * @see #configureAndRefreshWebApplicationContext 271 * @see #customizeContext 272 */ 273 @SuppressWarnings("unchecked") 274 public void setContextInitializers(ApplicationContextInitializer<?>... initializers) { 275 if (initializers != null) { 276 for (ApplicationContextInitializer<?> initializer : initializers) { 277 this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer); 278 } 279 } 280 } 281 282 283 /** 284 * Initialize Spring's web application context for the given servlet context, 285 * using the application context provided at construction time, or creating a new one 286 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 287 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 288 * @param servletContext current servlet context 289 * @return the new WebApplicationContext 290 * @see #ContextLoader(WebApplicationContext) 291 * @see #CONTEXT_CLASS_PARAM 292 * @see #CONFIG_LOCATION_PARAM 293 */ 294 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 295 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { 296 throw new IllegalStateException( 297 "Cannot initialize context because there is already a root application context present - " + 298 "check whether you have multiple ContextLoader* definitions in your web.xml!"); 299 } 300 301 Log logger = LogFactory.getLog(ContextLoader.class); 302 servletContext.log("Initializing Spring root WebApplicationContext"); 303 if (logger.isInfoEnabled()) { 304 logger.info("Root WebApplicationContext: initialization started"); 305 } 306 long startTime = System.currentTimeMillis(); 307 308 try { 309 // Store context in local instance variable, to guarantee that 310 // it is available on ServletContext shutdown. 311 if (this.context == null) { 312 this.context = createWebApplicationContext(servletContext); 313 } 314 if (this.context instanceof ConfigurableWebApplicationContext) { 315 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; 316 if (!cwac.isActive()) { 317 // The context has not yet been refreshed -> provide services such as 318 // setting the parent context, setting the application context id, etc 319 if (cwac.getParent() == null) { 320 // The context instance was injected without an explicit parent -> 321 // determine parent for root web application context, if any. 322 ApplicationContext parent = loadParentContext(servletContext); 323 cwac.setParent(parent); 324 } 325 configureAndRefreshWebApplicationContext(cwac, servletContext); 326 } 327 } 328 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 329 330 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 331 if (ccl == ContextLoader.class.getClassLoader()) { 332 currentContext = this.context; 333 } 334 else if (ccl != null) { 335 currentContextPerThread.put(ccl, this.context); 336 } 337 338 if (logger.isDebugEnabled()) { 339 logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + 340 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); 341 } 342 if (logger.isInfoEnabled()) { 343 long elapsedTime = System.currentTimeMillis() - startTime; 344 logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); 345 } 346 347 return this.context; 348 } 349 catch (RuntimeException ex) { 350 logger.error("Context initialization failed", ex); 351 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); 352 throw ex; 353 } 354 catch (Error err) { 355 logger.error("Context initialization failed", err); 356 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); 357 throw err; 358 } 359 } 360 361 /** 362 * Instantiate the root WebApplicationContext for this loader, either the 363 * default context class or a custom context class if specified. 364 * <p>This implementation expects custom contexts to implement the 365 * {@link ConfigurableWebApplicationContext} interface. 366 * Can be overridden in subclasses. 367 * <p>In addition, {@link #customizeContext} gets called prior to refreshing the 368 * context, allowing subclasses to perform custom modifications to the context. 369 * @param sc current servlet context 370 * @return the root WebApplicationContext 371 * @see ConfigurableWebApplicationContext 372 */ 373 protected WebApplicationContext createWebApplicationContext(ServletContext sc) { 374 Class<?> contextClass = determineContextClass(sc); 375 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 376 throw new ApplicationContextException("Custom context class [" + contextClass.getName() + 377 "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); 378 } 379 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 380 } 381 382 /** 383 * Return the WebApplicationContext implementation class to use, either the 384 * default XmlWebApplicationContext or a custom context class if specified. 385 * @param servletContext current servlet context 386 * @return the WebApplicationContext implementation class to use 387 * @see #CONTEXT_CLASS_PARAM 388 * @see org.springframework.web.context.support.XmlWebApplicationContext 389 */ 390 protected Class<?> determineContextClass(ServletContext servletContext) { 391 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 392 if (contextClassName != null) { 393 try { 394 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); 395 } 396 catch (ClassNotFoundException ex) { 397 throw new ApplicationContextException( 398 "Failed to load custom context class [" + contextClassName + "]", ex); 399 } 400 } 401 else { 402 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 403 try { 404 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); 405 } 406 catch (ClassNotFoundException ex) { 407 throw new ApplicationContextException( 408 "Failed to load default context class [" + contextClassName + "]", ex); 409 } 410 } 411 } 412 413 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { 414 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { 415 // The application context id is still set to its original default value 416 // -> assign a more useful id based on available information 417 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); 418 if (idParam != null) { 419 wac.setId(idParam); 420 } 421 else { 422 // Generate default id... 423 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 424 ObjectUtils.getDisplayString(sc.getContextPath())); 425 } 426 } 427 428 wac.setServletContext(sc); 429 String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); 430 if (configLocationParam != null) { 431 wac.setConfigLocation(configLocationParam); 432 } 433 434 // The wac environment's #initPropertySources will be called in any case when the context 435 // is refreshed; do it eagerly here to ensure servlet property sources are in place for 436 // use in any post-processing or initialization that occurs below prior to #refresh 437 ConfigurableEnvironment env = wac.getEnvironment(); 438 if (env instanceof ConfigurableWebEnvironment) { 439 ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); 440 } 441 442 customizeContext(sc, wac); 443 wac.refresh(); 444 } 445 446 /** 447 * Customize the {@link ConfigurableWebApplicationContext} created by this 448 * ContextLoader after config locations have been supplied to the context 449 * but before the context is <em>refreshed</em>. 450 * <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext) 451 * determines} what (if any) context initializer classes have been specified through 452 * {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and 453 * {@linkplain ApplicationContextInitializer#initialize invokes each} with the 454 * given web application context. 455 * <p>Any {@code ApplicationContextInitializers} implementing 456 * {@link org.springframework.core.Ordered Ordered} or marked with @{@link 457 * org.springframework.core.annotation.Order Order} will be sorted appropriately. 458 * @param sc the current servlet context 459 * @param wac the newly created application context 460 * @see #CONTEXT_INITIALIZER_CLASSES_PARAM 461 * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext) 462 */ 463 protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { 464 List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = 465 determineContextInitializerClasses(sc); 466 467 for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) { 468 Class<?> initializerContextClass = 469 GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); 470 if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { 471 throw new ApplicationContextException(String.format( 472 "Could not apply context initializer [%s] since its generic parameter [%s] " + 473 "is not assignable from the type of application context used by this " + 474 "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), 475 wac.getClass().getName())); 476 } 477 this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); 478 } 479 480 AnnotationAwareOrderComparator.sort(this.contextInitializers); 481 for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { 482 initializer.initialize(wac); 483 } 484 } 485 486 /** 487 * Return the {@link ApplicationContextInitializer} implementation classes to use 488 * if any have been specified by {@link #CONTEXT_INITIALIZER_CLASSES_PARAM}. 489 * @param servletContext current servlet context 490 * @see #CONTEXT_INITIALIZER_CLASSES_PARAM 491 */ 492 protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> 493 determineContextInitializerClasses(ServletContext servletContext) { 494 495 List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes = 496 new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>(); 497 498 String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM); 499 if (globalClassNames != null) { 500 for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { 501 classes.add(loadInitializerClass(className)); 502 } 503 } 504 505 String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM); 506 if (localClassNames != null) { 507 for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) { 508 classes.add(loadInitializerClass(className)); 509 } 510 } 511 512 return classes; 513 } 514 515 @SuppressWarnings("unchecked") 516 private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) { 517 try { 518 Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); 519 if (!ApplicationContextInitializer.class.isAssignableFrom(clazz)) { 520 throw new ApplicationContextException( 521 "Initializer class does not implement ApplicationContextInitializer interface: " + clazz); 522 } 523 return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz; 524 } 525 catch (ClassNotFoundException ex) { 526 throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex); 527 } 528 } 529 530 /** 531 * Template method with default implementation (which may be overridden by a 532 * subclass), to load or obtain an ApplicationContext instance which will be 533 * used as the parent context of the root WebApplicationContext. If the 534 * return value from the method is null, no parent context is set. 535 * <p>The main reason to load a parent context here is to allow multiple root 536 * web application contexts to all be children of a shared EAR context, or 537 * alternately to also share the same parent context that is visible to 538 * EJBs. For pure web applications, there is usually no need to worry about 539 * having a parent context to the root web application context. 540 * <p>The default implementation uses 541 * {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator}, 542 * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and 543 * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context 544 * which will be shared by all other users of ContextsingletonBeanFactoryLocator 545 * which also use the same configuration parameters. 546 * @param servletContext current servlet context 547 * @return the parent application context, or {@code null} if none 548 * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator 549 */ 550 protected ApplicationContext loadParentContext(ServletContext servletContext) { 551 ApplicationContext parentContext = null; 552 String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM); 553 String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM); 554 555 if (parentContextKey != null) { 556 // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml" 557 BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector); 558 Log logger = LogFactory.getLog(ContextLoader.class); 559 if (logger.isDebugEnabled()) { 560 logger.debug("Getting parent context definition: using parent context key of '" + 561 parentContextKey + "' with BeanFactoryLocator"); 562 } 563 this.parentContextRef = locator.useBeanFactory(parentContextKey); 564 parentContext = (ApplicationContext) this.parentContextRef.getFactory(); 565 } 566 567 return parentContext; 568 } 569 570 /** 571 * Close Spring's web application context for the given servlet context. If 572 * the default {@link #loadParentContext(ServletContext)} implementation, 573 * which uses ContextSingletonBeanFactoryLocator, has loaded any shared 574 * parent context, release one reference to that shared parent context. 575 * <p>If overriding {@link #loadParentContext(ServletContext)}, you may have 576 * to override this method as well. 577 * @param servletContext the ServletContext that the WebApplicationContext runs in 578 */ 579 public void closeWebApplicationContext(ServletContext servletContext) { 580 servletContext.log("Closing Spring root WebApplicationContext"); 581 try { 582 if (this.context instanceof ConfigurableWebApplicationContext) { 583 ((ConfigurableWebApplicationContext) this.context).close(); 584 } 585 } 586 finally { 587 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 588 if (ccl == ContextLoader.class.getClassLoader()) { 589 currentContext = null; 590 } 591 else if (ccl != null) { 592 currentContextPerThread.remove(ccl); 593 } 594 servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); 595 if (this.parentContextRef != null) { 596 this.parentContextRef.release(); 597 } 598 } 599 } 600 601 602 /** 603 * Obtain the Spring root web application context for the current thread 604 * (i.e. for the current thread's context ClassLoader, which needs to be 605 * the web application's ClassLoader). 606 * @return the current root web application context, or {@code null} 607 * if none found 608 * @see org.springframework.web.context.support.SpringBeanAutowiringSupport 609 */ 610 public static WebApplicationContext getCurrentWebApplicationContext() { 611 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 612 if (ccl != null) { 613 WebApplicationContext ccpt = currentContextPerThread.get(ccl); 614 if (ccpt != null) { 615 return ccpt; 616 } 617 } 618 return currentContext; 619 } 620 621}