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