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初始化时由容器调用servlet的init()方法,调用servlet的ini()方法时会将ServletConfig对象传递给servlet,当用户发出HTTP请求后,会被委派到web容器(Servlet容器),多个请求同时发起时交由Servlet 容器线程池处理,线程池中分配多个线程处理任务,每个线程执行一个单一的 Servlet 实例的 service() 方法。

2. DispatcherServlet

DispatcherServlet本质上也是一个Servlet,从继承关系上看,它继承了HttpServletBean,HttpServletBean中实现了init()方法,因为在Web容器启动时将调用它的init方法,该方法的作用:

  1. 将Servlet初始化参数(init-param)设置到该组件上,主要ServletConfig对象传递给servlet。
  2. 提供给子类初始化扩展点,initServletBean() 
DispatcherServlet结构图
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容器。

这两个容器的区别:

  1. ContextLoaderListener创建的WebApplicationContext是共享于整个Web应用程序的。
  2. 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框架初始化默认的使用的九大策略,这九大策略参考下篇文章。

 

 

posted @ 2018-07-24 00:12  有爱jj  阅读(9058)  评论(0编辑  收藏  举报