实现WebApplicationinitializer接口的类都可以在web应用程序启动时被加载。
那么来想一个问题:为什么实现了WebApplicationInitializer这个接口后,onStartup方法就会自动执行?
我们来简单分析一下它的实现原理,下面先贴上WebApplicationInitializer接口相关代码:
servlet的ServletContainerInitializer接口:
package javax.servlet;
import java.util.Set;
public interface ServletContainerInitializer {
/**
* Receives notification during startup of a web application of the classes
* within the web application that matched the criteria defined via the
* {@link javax.servlet.annotation.HandlesTypes} annotation.
*
* @param c The (possibly null) set of classes that met the specified
* criteria
* @param ctx The ServletContext of the web application in which the
* classes were discovered
*
* @throws ServletException If an error occurs
*/
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
SpringServletContainerInitializer 类(从本类开始以下代码都是spring的代码):
package org.springframework.web;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
/**
* 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
*/
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
AnnotationAwareOrderComparator.sort(initializers);
servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
WebApplicationInitializer接口(本文讨论的接口):
package org.springframework.web;
import javax.servlet.ServletContext;
import javax.servlet.ServletException
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;
}
javax.servlet.ServletContainerInitializer文件(在路径:spring-framework/spring-web/src/main/resources/META-INF/services/下)
该文件文件名就是“javax.servlet.ServletContainerInitializer”,其内容只有下面这行
org.springframework.web.SpringServletContainerInitializer
至此,相关代码/文件,都在上面给出了。
那么请看SpringServletContainerInitializer类,它实现了 Servlet 的ServletContainerInitializer接口,并且有"@HandlesTypes(WebApplicationInitializer.class) "注解,这个注解指明了ServletContainerInitializer接口可以使用的类。
并且必须在 spring-web-X.X.X.RELEASE-sources.jar 文件下,的META_INF/services路径下有个名为“javax.servlet.ServletContainerInitializer”的文件,文件中的内容与 ServletContainerIntializer的实现类全限定名相同(请参考上面贴出的代码)。这样,web应用启动时,程序就会找到WebApplicationInitializer这个接口的实现类,并调用onStartup方法。
如果说得不对的地方,请指正,谢谢