diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 10a8325c76c..c0eb4390886 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -28,17 +28,18 @@ import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.i18n.LocaleContext; import org.springframework.core.OrderComparator; import org.springframework.core.io.ClassPathResource; @@ -46,6 +47,7 @@ import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.ui.context.ThemeSource; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; +import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartHttpServletRequest; @@ -118,9 +120,15 @@ import org.springframework.web.util.WebUtils; * namespace, loading its own application context with mappings, handlers, etc. Only the root application context as * loaded by {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared. * + *
As of Spring 3.1, {@code DispatcherServlet} may now be injected with a web
+ * application context, rather than creating its own internally. This is useful in Servlet
+ * 3.0+ environments, which support programmatic registration of servlet instances. See
+ * {@link #DispatcherServlet(WebApplicationContext)} Javadoc for details.
+ *
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
+ * @author Chris Beams
* @see org.springframework.web.HttpRequestHandler
* @see org.springframework.web.servlet.mvc.Controller
* @see org.springframework.web.context.ContextLoaderListener
@@ -265,6 +273,70 @@ public class DispatcherServlet extends FrameworkServlet {
private List Calling {@link #setContextConfigLocation} (init-param 'contextConfigLocation')
+ * will dictate which XML files will be loaded by the
+ * {@linkplain #DEFAULT_CONTEXT_CLASS default XmlWebApplicationContext}
+ * Calling {@link #setContextClass} (init-param 'contextClass') overrides the
+ * default {@code XmlWebApplicationContext} and allows for specifying an alternative class,
+ * such as {@code AnnotationConfigWebApplicationContext}.
+ * Calling {@link #setContextInitializerClasses} (init-param 'contextInitializerClasses')
+ * indicates which {@code ApplicationContextInitializer} classes should be used to
+ * further configure the internal application context prior to refresh().
+ * @see #DispatcherServlet(WebApplicationContext)
+ */
+ public DispatcherServlet() {
+ super();
+ }
+
+ /**
+ * Create a new {@code DispatcherServlet} with the given web application context. This
+ * constructor is useful in Servlet 3.0+ environments where instance-based registration
+ * of servlets is possible through the {@link ServletContext#addServlet} API.
+ * Using this constructor indicates that the following properties / init-params
+ * will be ignored:
+ * The given web application context may or may not yet be {@linkplain
+ * ConfigurableApplicationContext#refresh() refreshed}. If it has not
+ * already been refreshed (the recommended approach), then the following will occur:
+ * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
+ * @param webApplicationContext the context to use
+ * @see #initWebApplicationContext
+ * @see #configureAndRefreshWebApplicationContext
+ * @see org.springframework.web.WebApplicationInitializer
+ */
+ public DispatcherServlet(WebApplicationContext webApplicationContext) {
+ super(webApplicationContext);
+ }
+
/**
* Set whether to detect all HandlerMapping beans in this servlet's context. Otherwise,
* just a single bean with name "handlerMapping" will be expected.
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
index 7e4b5d58223..d119b7c9188 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
@@ -18,6 +18,7 @@ package org.springframework.web.servlet;
import java.io.IOException;
import java.security.Principal;
+import java.util.TreeSet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@@ -35,7 +36,9 @@ import org.springframework.context.event.SourceFilteringListener;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleLocaleContext;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
@@ -100,6 +103,11 @@ import org.springframework.web.util.WebUtils;
* with XmlWebApplicationContext). The namespace can also be set explicitly via
* the "namespace" servlet init-param.
*
+ * As of Spring 3.1, {@code FrameworkServlet} may now be injected with a web
+ * application context, rather than creating its own internally. This is useful in Servlet
+ * 3.0+ environments, which support programmatic registration of servlet instances. See
+ * {@link #FrameworkServlet(WebApplicationContext)} Javadoc for details.
+ *
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
@@ -172,8 +180,77 @@ public abstract class FrameworkServlet extends HttpServletBean {
/** Flag used to detect whether onRefresh has already been called */
private boolean refreshEventReceived = false;
+ /** Comma-delimited ApplicationContextInitializer classnames set through init param */
private String contextInitializerClasses;
+ /** Actual ApplicationContextInitializer instances to apply to the context */
+ private TreeSet Calling {@link #setContextConfigLocation} (init-param 'contextConfigLocation')
+ * will dictate which XML files will be loaded by the
+ * {@linkplain #DEFAULT_CONTEXT_CLASS default XmlWebApplicationContext}
+ * Calling {@link #setContextClass} (init-param 'contextClass') overrides the
+ * default {@code XmlWebApplicationContext} and allows for specifying an alternative class,
+ * such as {@code AnnotationConfigWebApplicationContext}.
+ * Calling {@link #setContextInitializerClasses} (init-param 'contextInitializerClasses')
+ * indicates which {@link ApplicationContextInitializer} classes should be used to
+ * further configure the internal application context prior to refresh().
+ * @see #FrameworkServlet(WebApplicationContext)
+ */
+ public FrameworkServlet() {
+ }
+
+ /**
+ * Create a new {@code FrameworkServlet} with the given web application context. This
+ * constructor is useful in Servlet 3.0+ environments where instance-based registration
+ * of servlets is possible through the {@link ServletContext#addServlet} API.
+ * Using this constructor indicates that the following properties / init-params
+ * will be ignored:
+ * The given web application context may or may not yet be {@linkplain
+ * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation
+ * of {@link ConfigurableWebApplicationContext} and (b) has not
+ * already been refreshed (the recommended approach), then the following will occur:
+ * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
+ * @param webApplicationContext the context to use
+ * @see #initWebApplicationContext
+ * @see #configureAndRefreshWebApplicationContext
+ * @see org.springframework.web.WebApplicationInitializer
+ */
+ public FrameworkServlet(WebApplicationContext webApplicationContext) {
+ this.webApplicationContext = webApplicationContext;
+ }
/**
* Set the name of the ServletContext attribute which should be used to retrieve the
@@ -230,12 +307,25 @@ public abstract class FrameworkServlet extends HttpServletBean {
/**
* Specify the set of fully-qualified {@link ApplicationContextInitializer} class
* names, per the optional "contextInitializerClasses" servlet init-param.
- * @see #createWebApplicationContext
+ * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
+ * @see #applyInitializers(ConfigurableWebApplicationContext)
*/
public void setContextInitializerClasses(String contextInitializerClasses) {
this.contextInitializerClasses = contextInitializerClasses;
}
+ /**
+ * Specify which {@link ApplicationContextInitializer} instances should be used
+ * to initialize the application context used by this {@code FrameworkServlet}.
+ * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
+ * @see #applyInitializers(ConfigurableWebApplicationContext)
+ */
+ public void setContextInitializers(ApplicationContextInitializer Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
* @return the WebApplicationContext instance
+ * @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
- WebApplicationContext wac = findWebApplicationContext();
+ WebApplicationContext rootContext =
+ WebApplicationContextUtils.getWebApplicationContext(getServletContext());
+ WebApplicationContext wac = null;
+
+ if (this.webApplicationContext != null) {
+ // A context instance was injected at construction time -> use it
+ wac = this.webApplicationContext;
+ if (wac instanceof ConfigurableWebApplicationContext) {
+ ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
+ if (!cwac.isActive()) {
+ // The context has not yet been refreshed -> provide services such as
+ // setting the parent context, setting the application context id, etc
+ if (cwac.getParent() == null) {
+ // The context instance was injected without an explicit parent -> set
+ // the root application context (if any; may be null) as the parent
+ cwac.setParent(rootContext);
+ }
+ configureAndRefreshWebApplicationContext(cwac);
+ }
+ }
+ }
+ if (wac == null) {
+ // No context instance was injected at construction time -> see if one
+ // has been registered in the servlet context. If one exists, it is assumed
+ // that the parent context (if any) has already been set and that the
+ // user has performed any initialization such as setting the context id
+ wac = findWebApplicationContext();
+ }
if (wac == null) {
- // No fixed context defined for this servlet - create a local one.
- WebApplicationContext parent =
- WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- wac = createWebApplicationContext(parent);
+ // No context instance is defined for this servlet -> create a local one
+ wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
- // Apparently not a ConfigurableApplicationContext with refresh support:
- // triggering initial onRefresh manually here.
+ // Either the context is not a ConfigurableApplicationContext with refresh
+ // support or the context injected at construction time had already been
+ // refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
@@ -447,39 +564,48 @@ public abstract class FrameworkServlet extends HttpServletBean {
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
- // Assign the best possible id value.
- ServletContext sc = getServletContext();
- if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
- // Servlet <= 2.4: resort to name specified in web.xml, if any.
- String servletContextName = sc.getServletContextName();
- if (servletContextName != null) {
- wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
- "." + getServletName());
+ wac.setParent(parent);
+ wac.setConfigLocation(getContextConfigLocation());
+
+ configureAndRefreshWebApplicationContext(wac);
+
+ return wac;
+ }
+
+ protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
+
+ if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
+ // The application context id is still set to its original default value
+ // -> assign a more useful id based on available information
+ ServletContext sc = getServletContext();
+ if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
+ // Servlet <= 2.4: resort to name specified in web.xml, if any.
+ String servletContextName = sc.getServletContextName();
+ if (servletContextName != null) {
+ wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
+ "." + getServletName());
+ }
+ else {
+ wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
+ }
}
else {
- wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
+ // Servlet 2.5's getContextPath available!
+ wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath() +
+ "/" + getServletName());
}
}
- else {
- // Servlet 2.5's getContextPath available!
- wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath() +
- "/" + getServletName());
- }
- wac.setParent(parent);
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
- wac.setConfigLocation(getContextConfigLocation());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
postProcessWebApplicationContext(wac);
- initializeWebApplicationContext(wac);
+ applyInitializers(wac);
wac.refresh();
-
- return wac;
}
/**
@@ -506,10 +632,10 @@ public abstract class FrameworkServlet extends HttpServletBean {
* @param wac the configured WebApplicationContext (not refreshed yet)
* @see #createWebApplicationContext
* @see #postProcessWebApplicationContext
- * @see ConfigurableWebApplicationContext#refresh()
+ * @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings("unchecked")
- protected void initializeWebApplicationContext(ConfigurableWebApplicationContext wac) {
+ protected void applyInitializers(ConfigurableApplicationContext wac) {
if (this.contextInitializerClasses != null) {
String[] initializerClassNames = StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS);
for(String initializerClassName : initializerClassNames) {
@@ -522,9 +648,13 @@ public abstract class FrameworkServlet extends HttpServletBean {
String.format("Could not instantiate class [%s] specified via " +
"'contextInitializerClasses' init-param", initializerClassName), ex);
}
- initializer.initialize(wac);
+ this.contextInitializers.add(initializer);
}
}
+
+ for (ApplicationContextInitializer
+ *
+ *
+ *
+ * If the context has already been refreshed, none of the above will occur, under the
+ * assumption that the user has performed these actions (or not) per their specific
+ * needs.
+ *
+ *
+ *
+ *
+ * If the context has already been refreshed or does not implement
+ * {@code ConfigurableWebApplicationContext}, none of the above will occur under the
+ * assumption that the user has performed these actions (or not) per his or her
+ * specific needs.
+ *