springmvc web.xml配置之 -- DispatcherServlet
springMVC servlet配置与启动
看一下springmvc的web.xml常见配置:
<servlet>
<!-- 配置DispatcherServlet -->
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定spring mvc配置文件位置 不指定使用默认情况 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc.xml</param-value>
</init-param>
<!-- 设置启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- ServLet 匹配映射 -->
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
DispatcherServlet是一个特殊的Servlet,因此本文首先看看常规servlet的特性。
1. servlet特性
1.1 Servlet 启动过程
Servlet的启动过程如何? 难道和springmvc Ioc容器一样随web容器启动的时候就启动吗?
其实仔细想一想,可以猜出来Servlet并不一定是随web容器启动而创建,我们知道一个web容器中可能有非常多的Servlet,如果有百千个servlet,那服务器启动的时候就要实例化那么多个类,显然对于内存而言负载量相当的大。其实在web.xml的<servlet>
标签中可以通过<load-on-startup>
来控制Servlet的是否随web容器启动而初始化以及多个Servlet同时创建时的初始化顺序。
loadOnStartup设置分为三种情况:
-
loadOnStartup < 0 web容器启动的时候不实例化处理,servlet首次被调用时才被实例化 ,loadOnStartupm默认是这种(即没有配置时)。
-
loadOnStartup > 0 web容器启动的时候做实例化处理,顺序是由小到大,正整数小的先被实例化。
-
loadOnStartup = 0 web容器启动的时候做实例化处理,相当于是最大整数,因此web容器启动时,最后被实例化。
另外,<servlet-mapping>
会拦截请求,然后对应的servlet处理这个请求。上述配置<url-pattern>/</url-pattern>
表示拦截除了jsp以外的所有url,DispatchServlet将会处理这些请求。
1.2 Servlet 生命周期
- Servlet 通过调用 init () 方法进行初始化。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 通过调用 destroy() 方法终止(结束)。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
下图展示了一个典型的Servlet生命周期方案:
servlet初始化时由容器调用servlet的init()方法,调用servlet的ini()方法时会将ServletConfig对象传递给servlet,当用户发出HTTP请求后,会被委派到web容器(Servlet容器),多个请求同时发起时交由Servlet 容器线程池处理,线程池中分配多个线程处理任务,每个线程执行一个单一的 Servlet 实例的 service() 方法。
2. DispatcherServlet
DispatcherServlet本质上也是一个Servlet,从继承关系上看,它继承了HttpServletBean,HttpServletBean中实现了init()方法,因为在Web容器启动时将调用它的init方法,该方法的作用:
- 将Servlet初始化参数(init-param)设置到该组件上,主要ServletConfig对象传递给servlet。
- 提供给子类初始化扩展点,initServletBean()
2.1 Servlet 加载配置文件
(略)
2.1 初始化ServletBean
FrameworkServlet继承了HttpServletBean,它通过initServletBean()进行Web上下文初始化。
@Override protected final void initServletBean() throws ServletException { try { // 1.初始化web上下文 this.webApplicationContext = initWebApplicationContext(); // 2. 提供给子类初始化的扩展点 initFrameworkServlet(); }catch ... } }
initWebApplicationContext初始化WebApplicationContext:
protected WebApplicationContext initWebApplicationContext() { // 查找父容器,也就是web容器加载时ContextLoaderListener触发执行得到的IOC容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if(this.webApplicationContext != null) { // 1.创建DispatherServlet自己的容器 wac = this.webApplicationContext; if(wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac; if(!attrName.isActive()) { if(attrName.getParent() == null) { // 1.1 指定父容器,即ContextLoaderListener过程加载的IOC容器 attrName.setParent(rootContext); } // 1.2 执行wac.refresh(),也就是容器初始化 this.configureAndRefreshWebApplicationContext(attrName); } } } if(wac == null) { // 2.查找已经绑定的上下文 wac = this.findWebApplicationContext(); } if(wac == null) { // 3.如果没有找到相应的上下文,并指定父亲为ContextLoaderListener wac = this.createWebApplicationContext(rootContext); } if(!this.refreshEventReceived) { // 4.刷新上下文(执行一些初始化) this.onRefresh(wac); } if(this.publishContext) { String attrName1 = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName1, wac); if(this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]"); } } return wac; }
从上看出DispatcherServlet会维持一个自己的WebApplicationContext,ContextLoaderListener中创建ApplicationContext作为rootContext,传入DispatcherServlet的容器中,然后初始化自己的Ioc容器。
这两个容器的区别:
- ContextLoaderListener创建的WebApplicationContext是共享于整个Web应用程序的。
- DispatcherServlet创建的ApplicationContext主要用于和该Servlet相关的一些组件,比如Controller、ViewResovler等。
另外一点,上述代码中的this.onRefresh(wac)
过程会触发DispatchServlet初始话方法onRefresh(wac):
protected void onRefresh(ApplicationContext context) { this.initStrategies(context); } protected void initStrategies(ApplicationContext context) { this.initMultipartResolver(context); this.initLocaleResolver(context); this.initThemeResolver(context); this.initHandlerMappings(context); this.initHandlerAdapters(context); this.initHandlerExceptionResolvers(context); this.initRequestToViewNameTranslator(context); this.initViewResolvers(context); this.initFlashMapManager(context); }
这个过程就是Spring Web MVC框架初始化默认的使用的九大策略,这九大策略参考下篇文章。