diff --git a/spring-web/src/main/java/org/springframework/web/context/AbstractContextLoaderInitializer.java b/spring-web/src/main/java/org/springframework/web/context/AbstractContextLoaderInitializer.java index ecbf5a91da6..49622568bd9 100644 --- a/spring-web/src/main/java/org/springframework/web/context/AbstractContextLoaderInitializer.java +++ b/spring-web/src/main/java/org/springframework/web/context/AbstractContextLoaderInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -22,6 +22,7 @@ import javax.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContextInitializer; import org.springframework.web.WebApplicationInitializer; /** @@ -34,6 +35,7 @@ import org.springframework.web.WebApplicationInitializer; * * @author Arjen Poutsma * @author Chris Beams + * @author Juergen Hoeller * @since 3.2 */ public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { @@ -56,7 +58,9 @@ public abstract class AbstractContextLoaderInitializer implements WebApplication protected void registerContextLoaderListener(ServletContext servletContext) { WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { - servletContext.addListener(new ContextLoaderListener(rootAppContext)); + ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); + listener.setContextInitializers(getRootApplicationContextInitializers()); + servletContext.addListener(listener); } else { logger.debug("No ContextLoaderListener registered, as " + @@ -77,4 +81,15 @@ public abstract class AbstractContextLoaderInitializer implements WebApplication */ protected abstract WebApplicationContext createRootApplicationContext(); + /** + * Specify application context initializers to be applied to the root application + * context that the {@code ContextLoaderListener} is being created with. + * @since 4.2 + * @see #createRootApplicationContext() + * @see ContextLoaderListener#setContextInitializers + */ + protected ApplicationContextInitializer[] getRootApplicationContextInitializers() { + return null; + } + } diff --git a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java index 208f03971dc..9da29a744d2 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -193,6 +193,7 @@ public class ContextLoader { */ private static volatile WebApplicationContext currentContext; + /** * The root WebApplicationContext instance that this loader manages. */ @@ -204,6 +205,10 @@ public class ContextLoader { */ private BeanFactoryReference parentContextRef; + /** Actual ApplicationContextInitializer instances to apply to the context */ + private final List> contextInitializers = + new ArrayList>(); + /** * Create a new {@code ContextLoader} that will create a web application context @@ -260,6 +265,24 @@ public class ContextLoader { this.context = context; } + + /** + * Specify which {@link ApplicationContextInitializer} instances should be used + * to initialize the application context used by this {@code ContextLoader}. + * @since 4.2 + * @see #configureAndRefreshWebApplicationContext + * @see #customizeContext + */ + @SuppressWarnings("unchecked") + public void setContextInitializers(ApplicationContextInitializer... initializers) { + if (initializers != null) { + for (ApplicationContextInitializer initializer : initializers) { + this.contextInitializers.add((ApplicationContextInitializer) initializer); + } + } + } + + /** * Initialize Spring's web application context for the given servlet context, * using the application context provided at construction time, or creating a new one @@ -359,6 +382,37 @@ public class ContextLoader { return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } + /** + * Return the WebApplicationContext implementation class to use, either the + * default XmlWebApplicationContext or a custom context class if specified. + * @param servletContext current servlet context + * @return the WebApplicationContext implementation class to use + * @see #CONTEXT_CLASS_PARAM + * @see org.springframework.web.context.support.XmlWebApplicationContext + */ + protected Class determineContextClass(ServletContext servletContext) { + String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); + if (contextClassName != null) { + try { + return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); + } + 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); + } + } + } + protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value @@ -412,13 +466,6 @@ public class ContextLoader { protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { List>> initializerClasses = determineContextInitializerClasses(sc); - if (initializerClasses.isEmpty()) { - // no ApplicationContextInitializers have been declared -> nothing to do - return; - } - - ArrayList> initializerInstances = - new ArrayList>(); for (Class> initializerClass : initializerClasses) { Class initializerContextClass = @@ -430,46 +477,15 @@ public class ContextLoader { "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } - initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); + this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); } - AnnotationAwareOrderComparator.sort(initializerInstances); - for (ApplicationContextInitializer initializer : initializerInstances) { + AnnotationAwareOrderComparator.sort(this.contextInitializers); + for (ApplicationContextInitializer initializer : this.contextInitializers) { initializer.initialize(wac); } } - /** - * Return the WebApplicationContext implementation class to use, either the - * default XmlWebApplicationContext or a custom context class if specified. - * @param servletContext current servlet context - * @return the WebApplicationContext implementation class to use - * @see #CONTEXT_CLASS_PARAM - * @see org.springframework.web.context.support.XmlWebApplicationContext - */ - protected Class determineContextClass(ServletContext servletContext) { - String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); - if (contextClassName != null) { - try { - return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); - } - 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); - } - } - } - /** * Return the {@link ApplicationContextInitializer} implementation classes to use * if any have been specified by {@link #CONTEXT_INITIALIZER_CLASSES_PARAM}. diff --git a/spring-web/src/main/java/org/springframework/web/context/ContextLoaderListener.java b/spring-web/src/main/java/org/springframework/web/context/ContextLoaderListener.java index 83aff2d3ded..3b4fa7f28d5 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ContextLoaderListener.java +++ b/spring-web/src/main/java/org/springframework/web/context/ContextLoaderListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -23,18 +23,18 @@ import javax.servlet.ServletContextListener; * Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}. * Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}. * - *

This listener should be registered after - * {@link org.springframework.web.util.Log4jConfigListener} + *

This listener should be registered after {@link org.springframework.web.util.Log4jConfigListener} * in {@code web.xml}, if the latter is used. * *

As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web * application context via the {@link #ContextLoaderListener(WebApplicationContext)} - * constructor, allowing for programmatic configuration in Servlet 3.0+ environments. See - * {@link org.springframework.web.WebApplicationInitializer} for usage examples. + * constructor, allowing for programmatic configuration in Servlet 3.0+ environments. + * See {@link org.springframework.web.WebApplicationInitializer} for usage examples. * * @author Juergen Hoeller * @author Chris Beams * @since 17.02.2003 + * @see #setContextInitializers * @see org.springframework.web.WebApplicationInitializer * @see org.springframework.web.util.Log4jConfigListener */ @@ -98,6 +98,7 @@ public class ContextLoaderListener extends ContextLoader implements ServletConte super(context); } + /** * Initialize the root web application context. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 9256d0a8c89..69aec1d7daf 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -19,6 +19,7 @@ package org.springframework.web.servlet; import java.io.IOException; import java.security.Principal; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -182,7 +183,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic private String contextConfigLocation; /** Actual ApplicationContextInitializer instances to apply to the context */ - private final ArrayList> contextInitializers = + private final List> contextInitializers = new ArrayList>(); /** Comma-delimited ApplicationContextInitializer class names set through init param */ @@ -364,13 +365,15 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic /** * Specify which {@link ApplicationContextInitializer} instances should be used * to initialize the application context used by this {@code FrameworkServlet}. - * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) - * @see #applyInitializers(ConfigurableApplicationContext) + * @see #configureAndRefreshWebApplicationContext + * @see #applyInitializers */ @SuppressWarnings("unchecked") - public void setContextInitializers(ApplicationContextInitializer... contextInitializers) { - for (ApplicationContextInitializer initializer : contextInitializers) { - this.contextInitializers.add((ApplicationContextInitializer) initializer); + public void setContextInitializers(ApplicationContextInitializer... initializers) { + if (initializers != null) { + for (ApplicationContextInitializer initializer : initializers) { + this.contextInitializers.add((ApplicationContextInitializer) initializer); + } } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java index 2517b011d29..6f47082894d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -25,6 +25,7 @@ import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; +import org.springframework.context.ApplicationContextInitializer; import org.springframework.core.Conventions; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -51,6 +52,7 @@ import org.springframework.web.servlet.DispatcherServlet; * @author Arjen Poutsma * @author Chris Beams * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 3.2 */ public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer { @@ -64,7 +66,6 @@ public abstract class AbstractDispatcherServletInitializer extends AbstractConte @Override public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); - registerDispatcherServlet(servletContext); } @@ -80,7 +81,7 @@ public abstract class AbstractDispatcherServletInitializer extends AbstractConte */ protected void registerDispatcherServlet(ServletContext servletContext) { String servletName = getServletName(); - Assert.hasLength(servletName, "getServletName() may not return empty or null"); + Assert.hasLength(servletName, "getServletName() must not return empty or null"); WebApplicationContext servletAppContext = createServletApplicationContext(); Assert.notNull(servletAppContext, @@ -88,6 +89,8 @@ public abstract class AbstractDispatcherServletInitializer extends AbstractConte "context for servlet [" + servletName + "]"); DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext); + dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); + ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); Assert.notNull(registration, "Failed to register servlet with name '" + servletName + "'." + @@ -126,6 +129,18 @@ public abstract class AbstractDispatcherServletInitializer extends AbstractConte */ protected abstract WebApplicationContext createServletApplicationContext(); + /** + * Specify application context initializers to be applied to the servlet-specific + * application context that the {@code DispatcherServlet} is being created with. + * @since 4.2 + * @see #createServletApplicationContext() + * @see DispatcherServlet#setContextInitializers + * @see #getRootApplicationContextInitializers() + */ + protected ApplicationContextInitializer[] getServletApplicationContextInitializers() { + return null; + } + /** * Specify the servlet mapping(s) for the {@code DispatcherServlet} — * for example {@code "/"}, {@code "/app"}, etc. @@ -178,9 +193,9 @@ public abstract class AbstractDispatcherServletInitializer extends AbstractConte } private EnumSet getDispatcherTypes() { - return isAsyncSupported() ? - EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) : - EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE); + return (isAsyncSupported() ? + EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) : + EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE)); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java index b8e8630a5df..c54e84dc9e9 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -57,7 +57,7 @@ import static org.junit.Assert.*; * @since 12.08.2003 * @see org.springframework.web.context.support.Spr8510Tests */ -public final class ContextLoaderTests { +public class ContextLoaderTests { @Test public void testContextLoaderListenerWithDefaultContext() { @@ -155,6 +155,50 @@ public final class ContextLoaderTests { assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); } + @Test + public void testContextLoaderListenerWithProgrammaticInitializers() { + MockServletContext sc = new MockServletContext(""); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, + "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); + ContextLoaderListener listener = new ContextLoaderListener(); + listener.setContextInitializers(new TestContextInitializer(), new TestWebContextInitializer()); + listener.contextInitialized(new ServletContextEvent(sc)); + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); + TestBean testBean = wac.getBean(TestBean.class); + assertThat(testBean.getName(), equalTo("testName")); + assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); + } + + @Test + public void testContextLoaderListenerWithProgrammaticAndLocalInitializers() { + MockServletContext sc = new MockServletContext(""); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, + "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); + sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, TestContextInitializer.class.getName()); + ContextLoaderListener listener = new ContextLoaderListener(); + listener.setContextInitializers(new TestWebContextInitializer()); + listener.contextInitialized(new ServletContextEvent(sc)); + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); + TestBean testBean = wac.getBean(TestBean.class); + assertThat(testBean.getName(), equalTo("testName")); + assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); + } + + @Test + public void testContextLoaderListenerWithProgrammaticAndGlobalInitializers() { + MockServletContext sc = new MockServletContext(""); + sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, + "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); + sc.addInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM, TestWebContextInitializer.class.getName()); + ContextLoaderListener listener = new ContextLoaderListener(); + listener.setContextInitializers(new TestContextInitializer()); + listener.contextInitialized(new ServletContextEvent(sc)); + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); + TestBean testBean = wac.getBean(TestBean.class); + assertThat(testBean.getName(), equalTo("testName")); + assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); + } + @Test public void testRegisteredContextInitializerCanAccessServletContextParamsViaEnvironment() { MockServletContext sc = new MockServletContext(""); @@ -169,7 +213,7 @@ public final class ContextLoaderTests { } @Test - public void testContextLoaderListenerWithUnkownContextInitializer() { + public void testContextLoaderListenerWithUnknownContextInitializer() { MockServletContext sc = new MockServletContext(""); // config file doesn't matter. just a placeholder sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,