diff --git a/spring-web/src/main/java/org/springframework/web/WebApplicationInitializer.java b/spring-web/src/main/java/org/springframework/web/WebApplicationInitializer.java index 6cea24fc865..7e7c92eda22 100644 --- a/spring-web/src/main/java/org/springframework/web/WebApplicationInitializer.java +++ b/spring-web/src/main/java/org/springframework/web/WebApplicationInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -72,6 +72,9 @@ import javax.servlet.ServletException; * * } * + * As an alternative to the above, you can also extend from {@link + * org.springframework.web.servlet.support.AbstractDispatcherServletInitializer}. + * * As you can see, thanks to Servlet 3.0's new {@link ServletContext#addServlet} method * we're actually registering an instance of the {@code DispatcherServlet}, and * this means that the {@code DispatcherServlet} can now be treated like any other object @@ -82,7 +85,7 @@ import javax.servlet.ServletException; * are free to create and work with your Spring application contexts as necessary before * injecting them into the {@code DispatcherServlet}. * - *
Most major Spring Web componentry has been updated to support this style of + *
Most major Spring Web components have been updated to support this style of * registration. You'll find that {@code DispatcherServlet}, {@code FrameworkServlet}, * {@code ContextLoaderListener} and {@code DelegatingFilterProxy} all now support * constructor arguments. Even if a component (e.g. non-Spring, other third party) has not @@ -131,6 +134,9 @@ import javax.servlet.ServletException; * * } * + * As an alternative to the above, you can also extend from {@link + * org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer}. + * * Remember that {@code WebApplicationInitializer} implementations are detected * automatically -- so you are free to package them within your application as you * see fit. @@ -165,6 +171,9 @@ import javax.servlet.ServletException; * @author Chris Beams * @since 3.1 * @see SpringServletContainerInitializer + * @see org.springframework.web.context.AbstractContextLoaderInitializer + * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer + * @see org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer */ public interface WebApplicationInitializer { 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 new file mode 100644 index 00000000000..54defc908f7 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/context/AbstractContextLoaderInitializer.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2012 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.context; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.web.WebApplicationInitializer; + +/** + * Convenient base class for {@link WebApplicationInitializer} implementations that + * register a {@link ContextLoaderListener} in the servlet context. + * + *
The only method required to be implemented by subclasses is {@link + * #createRootApplicationContext()}, which gets invoked from {@link + * #registerContextLoaderListener(javax.servlet.ServletContext)}. + * + * @author Arjen Poutsma + * @author Chris Beams + * @since 3.2 + */ +public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { + + /** Logger available to subclasses. */ + protected final Log logger = LogFactory.getLog(getClass()); + + public void onStartup(ServletContext servletContext) throws ServletException { + this.registerContextLoaderListener(servletContext); + } + + /** + * Register a {@link ContextLoaderListener} against the given servlet context. The + * {@code ContextLoaderListener} is initialized with the application context returned + * from the {@link #createRootApplicationContext()} template method. + * @param servletContext the servlet context to register the listener against + */ + protected void registerContextLoaderListener(ServletContext servletContext) { + WebApplicationContext rootAppContext = this.createRootApplicationContext(); + if (rootAppContext != null) { + servletContext.addListener(new ContextLoaderListener(rootAppContext)); + } + else { + logger.debug("No ContextLoaderListener registered, as " + + "createRootApplicationContext() did not return an application context"); + } + } + + /** + * Create the "root" application context to be provided to the + * {@code ContextLoaderListener}. + *
The returned context is delegated to
+ * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will
+ * be established as the parent context for any {@code DispatcherServlet} application
+ * contexts. As such, it typically contains middle-tier services, data sources, etc.
+ * @return the root application context, or {@code null} if a root context is not
+ * desired
+ * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
+ */
+ protected abstract WebApplicationContext createRootApplicationContext();
+
+}
diff --git a/spring-web/src/test/java/org/springframework/web/context/ContextLoaderInitializerTests.java b/spring-web/src/test/java/org/springframework/web/context/ContextLoaderInitializerTests.java
new file mode 100644
index 00000000000..998df405994
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/web/context/ContextLoaderInitializerTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2002-2012 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.context;
+
+import java.util.EventListener;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.mock.web.MockServletContext;
+import org.springframework.web.context.support.StaticWebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test case for {@link AbstractContextLoaderInitializer}.
+ *
+ * @author Arjen Poutsma
+ */
+public class ContextLoaderInitializerTests {
+
+ private static final String BEAN_NAME = "myBean";
+
+ private AbstractContextLoaderInitializer initializer;
+
+ private MockServletContext servletContext;
+
+ private EventListener eventListener;
+
+ @Before
+ public void setUp() throws Exception {
+ servletContext = new MyMockServletContext();
+ initializer = new MyContextLoaderInitializer();
+ eventListener = null;
+ }
+
+ @Test
+ public void register() throws ServletException {
+ initializer.onStartup(servletContext);
+
+ assertTrue(eventListener instanceof ContextLoaderListener);
+ ContextLoaderListener cll = (ContextLoaderListener) eventListener;
+ cll.contextInitialized(new ServletContextEvent(servletContext));
+
+ WebApplicationContext applicationContext = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(servletContext);
+
+ assertTrue(applicationContext.containsBean(BEAN_NAME));
+ assertTrue(applicationContext.getBean(BEAN_NAME) instanceof MyBean);
+ }
+
+ private class MyMockServletContext extends MockServletContext {
+
+ @Override
+ public Concrete implementations are required to implement {@link #getRootConfigClasses()},
+ * {@link #getServletConfigClasses()}, as well as {@link #getServletMappings()}. Further
+ * template and customization methods are provided by {@link
+ * AbstractDispatcherServletInitializer}.
+ *
+ * @author Arjen Poutsma
+ * @author Chris Beams
+ * @since 3.2
+ */
+public abstract class AbstractAnnotationConfigDispatcherServletInitializer
+ extends AbstractDispatcherServletInitializer {
+
+ /**
+ * {@inheritDoc}
+ * This implementation creates an {@link AnnotationConfigWebApplicationContext},
+ * providing it the annotated classes returned by {@link #getRootConfigClasses()}.
+ * Returns {@code null} if {@link #getRootConfigClasses()} returns {@code null}.
+ */
+ @Override
+ protected WebApplicationContext createRootApplicationContext() {
+ Class>[] rootConfigClasses = this.getRootConfigClasses();
+ if (!ObjectUtils.isEmpty(rootConfigClasses)) {
+ AnnotationConfigWebApplicationContext rootAppContext =
+ new AnnotationConfigWebApplicationContext();
+ rootAppContext.register(rootConfigClasses);
+ return rootAppContext;
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * This implementation creates an {@link AnnotationConfigWebApplicationContext},
+ * providing it the annotated classes returned by {@link #getServletConfigClasses()}.
+ * @throws IllegalArgumentException if {@link #getServletConfigClasses()} returns
+ * empty or {@code null}
+ */
+ @Override
+ protected WebApplicationContext createServletApplicationContext() {
+ AnnotationConfigWebApplicationContext servletAppContext =
+ new AnnotationConfigWebApplicationContext();
+ Class>[] servletConfigClasses = this.getServletConfigClasses();
+ Assert.notEmpty(servletConfigClasses,
+ "getServletConfigClasses() did not return any configuration classes");
+
+ servletAppContext.register(servletConfigClasses);
+ return servletAppContext;
+ }
+
+ /**
+ * Specify {@link org.springframework.context.annotation.Configuration @Configuration}
+ * and/or {@link org.springframework.stereotype.Component @Component} classes to be
+ * provided to the {@linkplain #createRootApplicationContext() root application context}.
+ * @return the configuration classes for the root application context, or {@code null}
+ * if creation and registration of a root context is not desired
+ */
+ protected abstract Class>[] getRootConfigClasses();
+
+ /**
+ * Specify {@link org.springframework.context.annotation.Configuration @Configuration}
+ * and/or {@link org.springframework.stereotype.Component @Component} classes to be
+ * provided to the {@linkplain #createServletApplicationContext() dispatcher servlet
+ * application context}.
+ * @return the configuration classes for the dispatcher servlet application context
+ * (may not be empty or {@code null}).
+ */
+ protected abstract Class>[] getServletConfigClasses();
+
+}
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
new file mode 100644
index 00000000000..5f661d5b652
--- /dev/null
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2002-2012 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.servlet.support;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.springframework.util.Assert;
+import org.springframework.web.context.AbstractContextLoaderInitializer;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+
+/**
+ * Base class for {@link org.springframework.web.WebApplicationInitializer
+ * WebApplicationInitializer} implementations that register a {@link DispatcherServlet} in
+ * the servlet context.
+ *
+ * Concrete implementations are required to implement {@link
+ * #createServletApplicationContext()}, as well as {@link #getServletMappings()}, both of
+ * which gets invoked from {@link #registerDispatcherServlet(ServletContext)}. Further
+ * customization can be achieved by overriding
+ * {@link #customizeRegistration(ServletRegistration.Dynamic)}.
+ *
+ * Because this class extends from {@link AbstractContextLoaderInitializer}, concrete
+ * implementations are also required to implement {@link #createRootApplicationContext()}
+ * to set up a parent "root" application context. If a root context is
+ * not desired, implementations can simply return {@code null} in the
+ * {@code createRootApplicationContext()} implementation.
+ *
+ * @author Arjen Poutsma
+ * @author Chris Beams
+ * @since 3.2
+ */
+public abstract class AbstractDispatcherServletInitializer
+ extends AbstractContextLoaderInitializer {
+
+ /**
+ * The default servlet name. Can be customized by overriding {@link #getServletName}.
+ */
+ public static final String DEFAULT_SERVLET_NAME = "dispatcher";
+
+ @Override
+ public void onStartup(ServletContext servletContext) throws ServletException {
+ super.onStartup(servletContext);
+
+ this.registerDispatcherServlet(servletContext);
+ }
+
+ /**
+ * Register a {@link DispatcherServlet} against the given servlet context.
+ * This method will create a {@code DispatcherServlet} with the name returned by
+ * {@link #getServletName()}, initializing it with the application context returned
+ * from {@link #createServletApplicationContext()}, and mapping it to the patterns
+ * returned from {@link #getServletMappings()}.
+ * Further customization can be achieved by overriding {@link
+ * #customizeRegistration(ServletRegistration.Dynamic)}.
+ * @param servletContext the context to register the servlet against
+ */
+ protected void registerDispatcherServlet(ServletContext servletContext) {
+ String servletName = this.getServletName();
+ Assert.hasLength(servletName,
+ "getServletName() may not return empty or null");
+
+ WebApplicationContext servletAppContext = this.createServletApplicationContext();
+ Assert.notNull(servletAppContext,
+ "createServletApplicationContext() did not return an application " +
+ "context for servlet [" + servletName + "]");
+
+ DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
+
+ ServletRegistration.Dynamic registration =
+ servletContext.addServlet(servletName, dispatcherServlet);
+ registration.setLoadOnStartup(1);
+ registration.addMapping(getServletMappings());
+
+ this.customizeRegistration(registration);
+ }
+
+ /**
+ * Return the name under which the {@link DispatcherServlet} will be registered.
+ * Defaults to {@link #DEFAULT_SERVLET_NAME}.
+ * @see #registerDispatcherServlet(ServletContext)
+ */
+ protected String getServletName() {
+ return DEFAULT_SERVLET_NAME;
+ }
+
+ /**
+ * Create a servlet application context to be provided to the {@code DispatcherServlet}.
+ * The returned context is delegated to Spring's
+ * {@link DispatcherServlet#DispatcherServlet(WebApplicationContext)} As such, it
+ * typically contains controllers, view resolvers, locale resolvers, and other
+ * web-related beans.
+ * @see #registerDispatcherServlet(ServletContext)
+ */
+ protected abstract WebApplicationContext createServletApplicationContext();
+
+ /**
+ * Specify the servlet mapping(s) for the {@code DispatcherServlet}, e.g. '/', '/app',
+ * etc.
+ * @see #registerDispatcherServlet(ServletContext)
+ */
+ protected abstract String[] getServletMappings();
+
+ /**
+ * Optionally perform further registration customization once
+ * {@link #registerDispatcherServlet(ServletContext)} has completed.
+ * @param registration the {@code DispatcherServlet} registration to be customized
+ * @see #registerDispatcherServlet(ServletContext)
+ */
+ protected void customizeRegistration(ServletRegistration.Dynamic registration) {
+ }
+
+}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/AnnotationConfigDispatcherServletInitializerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/AnnotationConfigDispatcherServletInitializerTests.java
new file mode 100644
index 00000000000..a6cf3bdbfc7
--- /dev/null
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/AnnotationConfigDispatcherServletInitializerTests.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2002-2012 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.servlet.support;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.mock.web.MockServletContext;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test case for {@link AbstractAnnotationConfigDispatcherServletInitializer}.
+ *
+ * @author Arjen Poutsma
+ */
+public class AnnotationConfigDispatcherServletInitializerTests {
+
+ private static final String SERVLET_NAME = "myservlet";
+
+ private static final String ROLE_NAME = "role";
+
+ private static final String SERVLET_MAPPING = "/myservlet";
+
+ private AbstractDispatcherServletInitializer initializer;
+
+ private MockServletContext servletContext;
+
+ private Map