龙空技术网

DispatcherServlet类如何实例化

王朋code 156

前言:

此时各位老铁们对“类的实例化”大体比较看重,兄弟们都想要了解一些“类的实例化”的相关知识。那么小编也在网上搜集了一些关于“类的实例化””的相关文章,希望各位老铁们能喜欢,同学们一起来了解一下吧!

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、Listenerprotected 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了一个DispatcherServletprotected 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。

标签: #类的实例化