spring mvc DispatcherServlet类如何实例化、初始化的呢,并最终如何交给Tomcat容器的呢,Tomcat容器又如何将请求转给DispatcherServlet的
本文章针对的是spring5
spring mvc版本: spring-webmvc-5.3.6
·DispatcherServlet是springmvc的前端控制器,那它又是怎么实例化,初始化的呢,在哪里实例化的呢 ?
·DispatcherServlet其实还是个Servlet,所以这个Servlet对象最终还是要交给Servlet容器(比如Tomcat容器),那它又是怎么交给Tomcat容器的呢?
一、 DispatcherServlet实例化、初始化过程
AbstractDispatcherServletInitializer类的继承关系
为什么说AbstractDispatcherServletInitializer这个类呢, 在这个类中 有个方法 registerDispatcherServlet(), 从方法命名上很容易理解,就是 注册DispatcherServlet,往哪里注册,要么就是往 tomcat容器,要么就是往 spring ioc 容器,看代码就知道,其实就是往 tomcat容器(ServletContext)中注册 servlet,如下代码:
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
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);
}
到这里,我们知道了DispatcherServlet对象的创建代码,并注册到Tomcat容器中,但是它是在什么时候创建的呢。
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
在 AbstractDispatcherServletInitializer类中,有个onStartup()方法,调用了 registerDispatcherServlet(),
根据 AbstractDispatcherServletInitializer 类的继承关系,其父接口 WebApplicationInitializer接口只有一个 onStartup()。并看到如下代码:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {}
1.1. ServletContainerInitializer 类是 Servlet 规范中的定义 (javax.servlet.ServletContainerInitializer) , 粗略的说就是 tomcat容器启动过程中,会把标HandlesTypes (下面1.2 有介绍这个注解) 注解的类传递到ServletContainerInitializer中,进行实例化。
也就是说 tomcat 容器启动时,ServletContainerInitializer 实例化,然后把一系列标有HandlesTypes 注解的类,这里就是 SpringServletContainerInitializer 进行实例化,并被tomcat调用他的 onStartup()方法 。Set参数就是一系列 实现了 WebApplicationInitializer 接口的类,Set集合中的类统一进行 调用 initializer.onStartup(servletContext); 最终实现 tomcat 容器启动,将 DispatcherServlet 实例化,初始化,注册到tomcat中。
/***
* @param webAppInitializerClasses all implementations of
* {@link WebApplicationInitializer} found on the application classpath
* @param servletContext the servlet context to be initialized
**/
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = Collections.emptyList();
if (webAppInitializerClasses != null) {
initializers = new ArrayList<>(webAppInitializerClasses.size());
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)
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);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext); // 这里会最终 实例化 ,初始化 DispatcherServlet 对象,并注册到 Tomcat容器中
}
}
1.2 @HandlesTypes 注解 作用,参考其注释说明: 就是把标有 这个注解的类,交给Tomcat容器 ServletContainerInitializer 。 比如你定义了一个类 A,上面标有 HandlesType注解,启动时,A类就会被传递给 tomcat容器中进行实例化。
* This annotation is used to declare an array of application classes which are
* passed to a {@link javax.servlet.ServletContainerInitializer}.
二、http请求如何通过Tomcat容器转交给DispatcherServlet 对象的
知道了上面DispatcherServlet 的实例化,初始化,注册到tomcat容器中后,这里就很好理解了,tomcat 是servlet 规范的实现者。
根据下面的方法调用栈》》 》
at org.springframework.web.servlet.DispatcherServlet.noHandlerFound(DispatcherServlet.java:1275)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
大概的调用链路》》》
HttpServlet.service() -> FrameworkServlet.service() -> FrameworkServlet.service -> HttpServlet.service -> FrameworkServlet.doGet -> FrameworkServlet.processRequest -> DispatcherServlet.doService -> DispatcherServlet.doDispatch ->> Controller
DispatcherServlet 最终把请求,交给相应的Handler (Controller)进行业务处理
问题: 设想下,如果定义两个不同的Servlet,但是配置的UrlMapping 却是一样的,都注册到tomcat容器中,会怎么样,如果tomcat启动不报错的话,当http 请求发送过来的时候,会由那个Servlet来处理呢