DispatcherServlet类如何实例化

阿里云教程3个月前发布
16 0 0

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

DispatcherServlet本身也是一个Servlet,它的继承结构:

DispatcherServlet类如何实例化

因此,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的,它的继承结构:

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包下找了一下,果然不出所料:

DispatcherServlet类如何实例化

这样的话,我们就找到了最终根源了。真不容易啊~~

总结

第一根据java的SPI机制,在web服务启动的时候创建并运行了
ServletContainerInitializer实现类
的方法,从而可以我们的创建并调用
SpringServletContainerInitializer类中的onStartup方法。而
SpringServletContainerInitializer类中循环处理了所有实现了WebApplicationInitializer类,我们的
AbstractDispatcherServletInitializer实现类WebApplicationInitializer接口,重写了onStartup方法,因此可以调用
AbstractDispatcherServletInitializer实现类中注册DispatcherServlet相关的方法,从而实例化的我们的DispatcherServlet。

© 版权声明

相关文章

暂无评论

none
暂无评论...