spring mvc DispatcherServlet类如何实例化、初始化,Tomcat容器如何将请求转给DispatcherServlet对象

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来处理呢 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值