DispatcherServlet大家都很熟悉吧,这是SpringMVC的前端控制器,我们的请求都是由它进行路由转发到各个Controller中。那么对于DispatcherServlet是什么时候实例化的呢?
DispatcherServlet本身也是一个Servlet,它的继承结构:

因此,DispatcherServlet必然也逃不过Servlet的那套,即Servlet实例化。
servlet实例化调用过程
当一个web容器产生一个servlet实例时,它的基本顺序如下:
1、 Servlet容器(Tomcat)第一调用这个Servlet的init()方法,它将会初始化一个资源给servlet使用。列如一个logger。这个init()方法在整个servlet的生存周期只会被调用一次。
2、 init()方法初始化了一个对象,对象继承了
java.servlet.ServletConfig接口。这个对象使Servlet能够初始化那些被声明在部署描述符的参数。ServletConfig也使Servlet有权使用一个
javax.servlet.ServletContext 的对象,用这个对象servlet可以记录信息,分派请求到其他的web组件上并且能够使用在同一个应用上的其他web资源。
3、 当发起请求时,Servlet容器(Tomcat)调用这个Servlet的service()方法去响应Servlet的一些请求。
4、 也可以继承HttpServlet类,这样的话,当调用两个主要的HttpServlet的doPost(),doGet()方法,这个servlet容器将产生
javax..servlet.http.HttpServletRequest和HttpServletResponse 的对象并且把它们作为参数传到这些处理请求的方法中。
5、 管理一个servlet的生命周期,或者决定这个servlet实例对request请求的处理,在java虚拟机上的存在时间。当一个servlet容器开始移除一个servlet的时候将调用servlet的destroy()方法。
DispatcherServlet实例化
说完Servlet实例化的基本顺序后,我们来看DispatcherServlet是什么时候实例化的
AbstractDispatcherServletInitializer这个抽象类是用来实例化DispatcherServlet的,它的继承结构:

在
AbstractDispatcherServletInitializer中重写了WebApplicationInitializer接口的onStartup方法
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
//这里传递了ServletContext,我们可以添加Servlet、Filter、Listener
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
WebApplicationContext servletAppContext = createServletApplicationContext();
//这里简单粗暴的new了一个DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//向ServletContext中添加Servlet
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
//简单粗暴,直接new了一个DispatcherServlet
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
return new DispatcherServlet(servletAppContext);
}
OK,我们知道了DispatcherServlet是什么时候创建的,那
AbstractDispatcherServletInitializer又是什么时候调用的呢,我们知道
AbstractDispatcherServletInitializer实现了WebApplicationInitializer接口,那么WebApplicationInitializer接口什么时候调用的,DispatcherServlet便是什么时候创建的。
WebApplicationInitializer接口调用
WebApplicationInitializer是Spring提供的接口,目前JavaConfig配置方式在逐步取代xml配置方式。而WebApplicationInitializer可以看做是Web.xml的替代,它是一个接口。通过实现WebApplicationInitializer,在其中可以添加servlet,listener等,在加载Web项目的时候会加载这个接口实现类,从而起到web.xml一样的作用。
那么WebApplicationInitializer中的方法是什么时候被调用的呢?
在这个包下有另外一个类
SpringServletContainerInitializer。它是
ServletContainerInitializer实现类,而
ServletContainerInitializer是java提供的接口,
SpringServletContainerInitializer的实现如下:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
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 {
//通过反射实例化WebApplicationInitializer接口的实现类
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(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;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
//这里是所有的WebApplicationInitializer实现类
for (WebApplicationInitializer initializer : initializers) {
//开始遍历并逐个调用WebApplicationInitializer的onStartup方法
initializer.onStartup(servletContext);
}
}
//先判断webAppInitializerClasses这个Set是否为空。如果不为空的话,找到这个set中不是接口,不是抽象类,并且是
//WebApplicationInitializer接口实现类的类,将它们保存到list中。当这个list为空的时候,抛出异常。不为空的话就按照必定的顺序排序,
//并将它们按照必定的顺序实例化。调用其onStartup方法执行。到这里,就可以解释WebApplicationInitializer实现类的工作过程了。
}
这个类上还有一个注解@HandlesTypes,它由Servlet容器提供支持(实现),参数中指定的所有实现类,利用字节码扫描框架(例如ASM、BCEL)从classpath中扫描出来,放入集合,传给回调方法onStartup的第一个参数。
说到这里,
ServletContainerInitializer接口又是什么时候被调用的呢?感觉一层套一层没完了是吧
ServletContainerInitializer接口调用
这个接口是java提供的,对于这个接口,官方的解释是这样的:为了支持可以不使用web.xml,提供了
ServletContainerInitializer,它可以通过SPI机制,当启动web容器的时候,会自动到添加的相应jar包下找到META-INF/services下以
ServletContainerInitializer的全路径名称命名的文件,它的内容为
ServletContainerInitializer实现类的全路径,将它们启动并运行这个实现类中指定的方法。既然这样的话,那么
SpringServletContainerInitializer作为
ServletContainerInitializer的实现类,它的jar包下也应该有相应的文件。
然后我就去对应的web包下找了一下,果然不出所料:

这样的话,我们就找到了最终根源了。真不容易啊~~
总结
第一根据java的SPI机制,在web服务启动的时候创建并运行了
ServletContainerInitializer实现类的方法,从而可以我们的创建并调用
SpringServletContainerInitializer类中的onStartup方法。而
SpringServletContainerInitializer类中循环处理了所有实现了WebApplicationInitializer类,我们的
AbstractDispatcherServletInitializer实现类WebApplicationInitializer接口,重写了onStartup方法,因此可以调用
AbstractDispatcherServletInitializer实现类中注册DispatcherServlet相关的方法,从而实例化的我们的DispatcherServlet。