Introduce Servlet 3.0 WebApplicationInitializer
WebApplicationInitializer provides a programmatic alternative to the traditional WEB-INF/web.xml servlet container deployment descriptor for Servlet API 3.0+ environments. This is done by building atop the new ServletContainerInitializer support in Servlet 3.0. See SpringServletContainerInitializer for complete details. And see WebApplicationInitializer Javadoc for typical usage examples. Issue: SPR-7672
This commit is contained in:
parent
2b51759113
commit
c9e67b2ef2
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 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;
|
||||||
|
|
||||||
|
import static org.springframework.beans.BeanUtils.instantiateClass;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContainerInitializer;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.annotation.HandlesTypes;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based
|
||||||
|
* configuration of the servlet container using Spring's {@link WebApplicationInitializer}
|
||||||
|
* SPI as opposed to (or possibly in combination with) the traditional
|
||||||
|
* {@code web.xml}-based approach.
|
||||||
|
*
|
||||||
|
* <h2>Mechanism of Operation</h2>
|
||||||
|
* This class will be loaded and instantiated and have its {@link #onStartup} method
|
||||||
|
* invoked by any Servlet 3.0-compliant container during container startup assuming that
|
||||||
|
* the {@code spring-web} module JAR is present on the classpath. This occurs through the
|
||||||
|
* JAR Services API {@link ServiceLoader#load(Class)} method detecting the
|
||||||
|
* {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer}
|
||||||
|
* service provider configuration file. See the
|
||||||
|
* <a href="http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider">
|
||||||
|
* JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0
|
||||||
|
* Final Draft specification for complete details.
|
||||||
|
*
|
||||||
|
* <h3>when in combination with {@code web.xml}</h3>
|
||||||
|
* <p>If a web application does include a {@code WEB-INF/web.xml} file, it is important to
|
||||||
|
* understand that neither this nor any other {@code ServletContextInitializer} will be
|
||||||
|
* processed unless the {@code <web-app>} element's {@code version} attribute is >= "3.0"
|
||||||
|
* and the {@code xsi:schemaLocation} for "http://java.sun.com/xml/ns/javaee" is set to
|
||||||
|
* "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd".
|
||||||
|
*
|
||||||
|
* <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2>
|
||||||
|
* Spring's {@code WebApplicationInitializer} SPI consists of just one method:
|
||||||
|
* {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally
|
||||||
|
* quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}:
|
||||||
|
* simply put, {@code SpringServletContainerInitializer} is responsible for instantiating
|
||||||
|
* and delegating the {@code ServletContext} to any user-defined
|
||||||
|
* {@code WebApplicationInitializer} implementations. It is then the responsibility of
|
||||||
|
* each {@code WebApplicationInitializer} to do the actual work of initializing the
|
||||||
|
* {@code ServletContext}. The exact process of delegation is described in detail in the
|
||||||
|
* {@link #onStartup} documentation below.
|
||||||
|
*
|
||||||
|
* <h2>General Notes</h2>
|
||||||
|
* In general, this class should be viewed as <em>supporting infrastructure</em> for
|
||||||
|
* the more important and user-facing {@code WebApplicationInitializer} SPI. Taking
|
||||||
|
* advantage of this container initializer is also completely <em>optional</em>: while
|
||||||
|
* it is true that this initializer will be loaded and invoked under all Servlet 3.0+
|
||||||
|
* runtimes, it remains the user's choice whether to make any
|
||||||
|
* {@code WebApplicationInitializer} implementations available on the classpath. If no
|
||||||
|
* {@code WebApplicationInitializer} types are detected, this container initializer will
|
||||||
|
* have no effect.
|
||||||
|
*
|
||||||
|
* <p>Note that use of this container initializer and of {@code WebApplicationInitializer}
|
||||||
|
* is not in any way "tied" to Spring MVC other than the fact that the types are shipped
|
||||||
|
* in the {@code spring-web} module JAR. Rather, they can be considered general-purpose
|
||||||
|
* in their ability to facilitate convenient code-based configuration of the
|
||||||
|
* {@code ServletContext}. Said another way, any servlet, listener, or filter may be
|
||||||
|
* registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific
|
||||||
|
* components.
|
||||||
|
*
|
||||||
|
* <p>This class is not designed for nor intended to be extended. It should be considered
|
||||||
|
* an internal type, with {@code WebApplicationInitializer} being the public-facing SPI.
|
||||||
|
*
|
||||||
|
* <h2>See Also</h2>
|
||||||
|
* See {@link WebApplicationInitializer} Javadoc for examples and detailed usage
|
||||||
|
* recommendations.<p>
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.1
|
||||||
|
* @see #onStartup(Set, ServletContext)
|
||||||
|
* @see WebApplicationInitializer
|
||||||
|
*/
|
||||||
|
@HandlesTypes(WebApplicationInitializer.class)
|
||||||
|
public class SpringServletContainerInitializer implements ServletContainerInitializer {
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(SpringServletContainerInitializer.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
|
||||||
|
* implementations present on the application classpath.
|
||||||
|
*
|
||||||
|
* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
|
||||||
|
* Servlet 3.0+ containers will automatically scan the classpath for implementations
|
||||||
|
* of Spring's {@code WebApplicationInitializer} interface and provide the set of all
|
||||||
|
* such types to the {@code webAppInitializerClasses} parameter of this method.
|
||||||
|
*
|
||||||
|
* <p>If no {@code WebApplicationInitializer} implementations are found on the
|
||||||
|
* classpath, this method is effectively a no-op. An INFO-level log message will be
|
||||||
|
* issued notifying the user that the {@code ServletContainerInitializer} has indeed
|
||||||
|
* been invoked, but that no {@code WebApplicationInitializer} implementations were
|
||||||
|
* found.
|
||||||
|
*
|
||||||
|
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
|
||||||
|
* they will be instantiated (and <em>sorted</em> if the @{@link
|
||||||
|
* org.springframework.core.annotation.Order Order} annotation is present or
|
||||||
|
* the {@link org.springframework.core.Ordered Ordered} interface has been
|
||||||
|
* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
|
||||||
|
* method will be invoked on each instance, delegating the {@code ServletContext} such
|
||||||
|
* that each instance may register and configure servlets such as Spring's
|
||||||
|
* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}
|
||||||
|
* or any other Servlet API componentry such as filters.
|
||||||
|
*
|
||||||
|
* @param webAppInitializerClasses all implementations of
|
||||||
|
* {@link WebApplicationInitializer} found on the application classpath.
|
||||||
|
* @param servletContext the servlet context to be initialized
|
||||||
|
* @see WebApplicationInitializer#onStartup(ServletContext)
|
||||||
|
* @see AnnotationAwareOrderComparator
|
||||||
|
*/
|
||||||
|
public void onStartup(Set<Class<?>> webAppInitializerClasses,
|
||||||
|
ServletContext servletContext) throws ServletException {
|
||||||
|
|
||||||
|
Set<WebApplicationInitializer> initializers =
|
||||||
|
new TreeSet<WebApplicationInitializer>(new AnnotationAwareOrderComparator());
|
||||||
|
|
||||||
|
for (Class<?> waiClass : webAppInitializerClasses) {
|
||||||
|
if (Modifier.isAbstract(waiClass.getModifiers())) {
|
||||||
|
// the class is not instantiable (i.e. abstract or an interface) -> skip it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
initializers.add(instantiateClass(waiClass, WebApplicationInitializer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initializers.isEmpty()) {
|
||||||
|
logger.info("Detected no WebApplicationInitializer types on the classpath: exiting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Delegating ServletContext to the following " +
|
||||||
|
"WebApplicationInitializer instances: " + initializers);
|
||||||
|
|
||||||
|
for (WebApplicationInitializer initializer : initializers) {
|
||||||
|
initializer.onStartup(servletContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 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;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented in Servlet 3.0+ environments in order to configure the
|
||||||
|
* {@link ServletContext} programmatically -- as opposed to (or possibly in conjunction
|
||||||
|
* with) the traditional {@code web.xml}-based approach.
|
||||||
|
*
|
||||||
|
* <p>Implementations of this SPI will be detected automatically by the
|
||||||
|
* {@link SpringServletContainerInitializer}, which itself is automatically bootstrapped
|
||||||
|
* by Servlet 3.0 container. See {@linkplain SpringServletContainerInitializer its Javadoc}
|
||||||
|
* for details on this bootstrapping mechanism.
|
||||||
|
*
|
||||||
|
* <h2>Example</h2>
|
||||||
|
* <h3>The traditional, XML-based approach</h3>
|
||||||
|
* Most Spring users building a web application will need to register Spring's {@code
|
||||||
|
* DispatcherServlet}. For reference, in WEB-INF/web.xml, this would typically be done as
|
||||||
|
* follows:
|
||||||
|
* <pre class="code">
|
||||||
|
* {@code
|
||||||
|
* <servlet>
|
||||||
|
* <servlet-name>dispatcher</servlet-name>
|
||||||
|
* <servlet-class>
|
||||||
|
* org.springframework.web.servlet.DispatcherServlet
|
||||||
|
* </servlet-class>
|
||||||
|
* <init-param>
|
||||||
|
* <param-name>contextConfigLocation</param-name>
|
||||||
|
* <param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
|
||||||
|
* </init-param>
|
||||||
|
* <load-on-startup>1</load-on-startup>
|
||||||
|
* </servlet>
|
||||||
|
*
|
||||||
|
* <servlet-mapping>
|
||||||
|
* <servlet-name>dispatcher</servlet-name>
|
||||||
|
* <url-pattern>/main</url-pattern>
|
||||||
|
* </servlet-mapping>}</pre>
|
||||||
|
*
|
||||||
|
* <h3>The code-based approach with {@code WebApplicationInitializer}</h3>
|
||||||
|
* Here is the equivalent {@code DispatcherServlet} registration logic,
|
||||||
|
* {@code WebApplicationInitializer}-style:
|
||||||
|
* <pre class="code">
|
||||||
|
* public class MyWebAppInitializer implements WebApplicationInitializer {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void onStartup(ServletContext container) {
|
||||||
|
* XmlWebApplicationContext appContext = new XmlWebApplicationContext()
|
||||||
|
* appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
|
||||||
|
*
|
||||||
|
* ServletRegistration.Dynamic dispatcher =
|
||||||
|
* container.addServlet("dispatcher", new DispatcherServlet(appContext));
|
||||||
|
* dispatcher.setLoadOnStartup(1);
|
||||||
|
* dispatcher.addMapping("/main");
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* As you can see, thanks to Servlet 3.0's new {@link ServletContext#addServlet} method
|
||||||
|
* we're actually registering an <em>instance</em> of the {@code DispatcherServlet}, and
|
||||||
|
* this means that the {@code DispatcherServlet} can now be treated like any other object
|
||||||
|
* -- receiving constructor injection of its application context in this case.
|
||||||
|
*
|
||||||
|
* <p>This style is both simpler and more concise. There is no concern for dealing with
|
||||||
|
* init-params, etc, just normal JavaBean-style properties and constructor arguments. You
|
||||||
|
* are free to create and work with your Spring application contexts as necessary before
|
||||||
|
* injecting them into the {@code DispatcherServlet}.
|
||||||
|
*
|
||||||
|
* <p>Most major Spring Web componentry has 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
|
||||||
|
* been specifically updated for use within {@code WebApplicationInitializers}, they still
|
||||||
|
* may be used in any case. The Servlet 3.0 {@code ServletContext} API allows for setting
|
||||||
|
* init-params, context-params, etc programmatically.
|
||||||
|
*
|
||||||
|
* <h2>A 100% code-based approach to configuration</h2>
|
||||||
|
* In the example above, {@code WEB-INF/web.xml} was successfully replaced with code in
|
||||||
|
* the form of a {@code WebApplicationInitializer}, but the actual
|
||||||
|
* {@code dispatcher-config.xml} Spring configuration remained XML-based.
|
||||||
|
* {@code WebApplicationInitializer} is a perfect fit for use with Spring's code-based
|
||||||
|
* {@code @Configuration} classes. See @{@link
|
||||||
|
* org.springframework.context.annotation.Configuration Configuration} Javadoc for
|
||||||
|
* complete details, but the following example demonstrates refactoring to use Spring's
|
||||||
|
* {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||||
|
* AnnotationConfigWebApplicationContext} in lieu of {@code XmlWebApplicationContext}, and
|
||||||
|
* user-defined {@code @Configuration} classes {@code AppConfig} and
|
||||||
|
* {@code DispatcherConfig} instead of Spring XML files. This example also goes a bit
|
||||||
|
* beyond those above to demonstrate typical configuration of the 'root' application
|
||||||
|
* context and registration of the {@code ContextLoaderListener}:
|
||||||
|
* <pre class="code">
|
||||||
|
* public class MyWebAppInitializer implements WebApplicationInitializer {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void onStartup(ServletContext container) {
|
||||||
|
* // Create the 'root' Spring application context
|
||||||
|
* AnnotationConfigWebApplicationContext rootContext =
|
||||||
|
* new AnnotationConfigWebApplicationContext();
|
||||||
|
* rootContext.register(AppConfig.class);
|
||||||
|
*
|
||||||
|
* // Manage the lifecycle of the root application context
|
||||||
|
* container.addListener(new ContextLoaderListener(rootContext));
|
||||||
|
*
|
||||||
|
* // Create the dispatcher servlet's Spring application context
|
||||||
|
* AnnotationConfigWebApplicationContext dispatcherContext =
|
||||||
|
* new AnnotationConfigWebApplicationContext();
|
||||||
|
* dispatcherContext.register(DispatcherConfig.class);
|
||||||
|
*
|
||||||
|
* // Register and map the dispatcher servlet
|
||||||
|
* ServletRegistration.Dynamic dispatcher =
|
||||||
|
* container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
|
||||||
|
* dispatcher.setLoadOnStartup(1);
|
||||||
|
* dispatcher.addMapping("/main");
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* Remember that {@code WebApplicationInitializer} implementations are <em>detected
|
||||||
|
* automatically</em> -- so you are free to package them within your application as you
|
||||||
|
* see fit.
|
||||||
|
*
|
||||||
|
* <h2>Ordering {@code WebApplicationInitializer} execution</h2>
|
||||||
|
* {@code WebApplicationInitializer} implementations may optionally be annotated at the
|
||||||
|
* class level with Spring's @{@link org.springframework.core.annotation.Order Order}
|
||||||
|
* annotation or may implement Spring's {@link org.springframework.core.Ordered Ordered}
|
||||||
|
* interface. If so, the initializers will be ordered prior to invocation. This provides
|
||||||
|
* a mechanism for users to ensure the order in which servlet container initialization
|
||||||
|
* occurs. Use of this feature is expected to be rare, as typical applications will likely
|
||||||
|
* centralize all container initialization within a single {@code WebApplicationInitializer}.
|
||||||
|
*
|
||||||
|
* <h2>Caveats</h2>
|
||||||
|
*
|
||||||
|
* <h3>web.xml versioning</h3>
|
||||||
|
* <p>{@code WEB-INF/web.xml} and {@code WebApplicationInitializer} use are not mutually
|
||||||
|
* exclusive; for example, web.xml can register one servlet, and a {@code
|
||||||
|
* WebApplicationInitializer} can register another. An initializer can even
|
||||||
|
* <em>modify</em> registrations performed in {@code web.xml} through the
|
||||||
|
* {@link ServletContext#getServletRegistration(String)} method and its similiars.
|
||||||
|
* <strong>However, if {@code WEB-INF/web.xml} is present in the application, its
|
||||||
|
* {@code version} attribute must be set to "3.0" or greater, otherwise {@code
|
||||||
|
* ServletContainerInitializer} bootstrapping will be ignored by the servlet container.
|
||||||
|
* </strong>
|
||||||
|
*
|
||||||
|
* <h3>Mapping to '/' under Tomcat</h3>
|
||||||
|
* <p>Apache Tomcat maps its internal {@code DefaultServlet} to "/", and on Tomcat versions
|
||||||
|
* <= 7.0.14, this servlet mapping <em>cannot be overridden programmatically</em>. This
|
||||||
|
* is a <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=51278">known issue</a>
|
||||||
|
* and scheduled to be addressed in the next revision of Tomcat. Overriding the "/"
|
||||||
|
* servlet mapping has been tested successfully on GlassFish 3.1.<p>
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.1
|
||||||
|
* @see SpringServletContainerInitializer
|
||||||
|
*/
|
||||||
|
public interface WebApplicationInitializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the given {@link ServletContext} with any servlets, filters, listeners
|
||||||
|
* context-params and attributes necessary for initializing this web application. See
|
||||||
|
* examples {@linkplain WebApplicationInitializer above}.
|
||||||
|
* @param servletContext the {@code ServletContext} to initialize
|
||||||
|
* @throws ServletException if any call against the given {@code ServletContext}
|
||||||
|
* throws a {@code ServletException}
|
||||||
|
*/
|
||||||
|
void onStartup(ServletContext servletContext) throws ServletException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -37,14 +37,19 @@ import org.springframework.web.context.ContextLoader;
|
||||||
* as for classpath scanning (specifying base packages as config location).
|
* as for classpath scanning (specifying base packages as config location).
|
||||||
*
|
*
|
||||||
* <p>This is essentially the equivalent of
|
* <p>This is essentially the equivalent of
|
||||||
* {@link org.springframework.context.annotation.AnnotationConfigApplicationContext}
|
* {@link org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||||
* for a web environment.
|
* AnnotationConfigApplicationContext} for a web environment.
|
||||||
*
|
*
|
||||||
* <p>To make use of this application context, the
|
* <p>To make use of this application context, the
|
||||||
* {@linkplain ContextLoader#CONTEXT_CLASS_PARAM "contextClass"} context-param for
|
* {@linkplain ContextLoader#CONTEXT_CLASS_PARAM "contextClass"} context-param for
|
||||||
* ContextLoader and/or "contextClass" init-param for FrameworkServlet must be set to
|
* ContextLoader and/or "contextClass" init-param for FrameworkServlet must be set to
|
||||||
* the fully-qualified name of this class.
|
* the fully-qualified name of this class.
|
||||||
*
|
*
|
||||||
|
* <p>As of Spring 3.1, this class may also be directly instantiated and injected into
|
||||||
|
* Spring's {@code DispatcherServlet} or {@code ContextLoaderListener} when using the
|
||||||
|
* new {@link org.springframework.web.WebApplicationInitializer WebApplicationInitializer}
|
||||||
|
* code-based alternative to {@code web.xml}. See its Javadoc for details and usage examples.
|
||||||
|
*
|
||||||
* <p>Unlike {@link XmlWebApplicationContext}, no default configuration class locations
|
* <p>Unlike {@link XmlWebApplicationContext}, no default configuration class locations
|
||||||
* are assumed. Rather, it is a requirement to set the
|
* are assumed. Rather, it is a requirement to set the
|
||||||
* {@linkplain ContextLoader#CONFIG_LOCATION_PARAM "contextConfigLocation"}
|
* {@linkplain ContextLoader#CONFIG_LOCATION_PARAM "contextConfigLocation"}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
org.springframework.web.SpringServletContainerInitializer
|
||||||
Loading…
Reference in New Issue