Servlet 3.0以上支持JavaConfig配置web.xml
1、web容器启动
2、web容器调用javax.servlet.ServletContainerInitializer接口下的onStartup方法
package javax.servlet; import java.util.Set; public interface ServletContainerInitializer { public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; }
官方对该接口的解释如下:
An instance of the ServletContainerInitializer is looked up via the jar services API by the container at container / application startup time. The framework providing an implementation of the ServletContainerInitializer MUST bundle in the META-INF/services directory of the jar file a file called javax.servlet.ServletContainerInitializer, as per the jar services API, that points to the implementation class of the ServletContainerInitializer.
大意为:
为了支持不使用web.xml,
web容器在启动时会扫描所有ServletContainerInitializer的实现,并调用其onStartup方法.
扫描的方法为在所有jar包下查找路径为META-INF/services/ServletContainerInitializer文件,根据文件内的全路径进行实例化.
org.springframework.web下就有这样一个文件:
通过这个路径,我们就能找到ServletContainerInitializer的实现类:
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 { @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); } } }
内容挺简单的,就是遍历一个Class集合,如果是WebApplicationInitializer的子类且不是接口或抽象类就实例化。
如果没有一个实例化成功的话,就会输出"org.apache.catalina.core.ApplicationContext.log No Spring WebApplicationInitializer types detected on classpath",
否则就挨个调用实例的onStartup方法.
那么明明参数1的泛型并没有指定某个类,这个WebApplicationInitializer是哪来的呢?
文档内还有一段也很重要:
In addition to the ServletContainerInitializer we also have an annotation - HandlesTypes. The HandlesTypes annotation on the implementation of the ServletContainerInitializer is used to express interest in classes that may have anotations (type, method or field level annotations) specified in the value of the HandlesTypes or if it extends / implements one those classes anywhere in the class’ super types. The container uses the HandlesTypes annotation to determine when to invoke the initializer's onStartup method. When examining the classes of an application to see if they match any of the criteria specified by the HandlesTypes annotation of a ServletContainerInitializer, the container may run into class loading problems if one or more of the application's optional JAR files are missing. Since the container is not in a position to decide whether these types of class loading failures will prevent the application from working correctly, it must ignore them, while at the same time providing a configuration option that would log them.
大意为:
可以是用@HadnlesTypes注解来注入某个类.
这个类及其子类的class将被web容器自动扫描并传入webAppInitializerClasses这个参数上。
3、通过这一流程,最终我们自己配置的WebApplicationInitializer就被调用了:
import org.springframework.web.WebApplicationInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; /** * @author ParanoidCAT * @since JDK 1.8 */ public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { // TODO 这里写实现内容 } }
4、通过源码我们也能知道,如果没有通过JavaConfig配置web.xml,context的log会输出"org.apache.catalina.core.ApplicationContext.log No Spring WebApplicationInitializer types detected on classpath",然后去读取web.xml