欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring MVC之DispatcherServlet详解_动力节点Java学院整理

程序员文章站 2022-06-11 14:21:50
dispatcherservlet作用 dispatcherservlet是前端控制器设计模式的实现,提供spring web mvc的集中访问点,而且负责职责的分派...

dispatcherservlet作用

dispatcherservlet是前端控制器设计模式的实现,提供spring web mvc的集中访问点,而且负责职责的分派,而且与spring ioc容器无缝集成,从而可以获得spring的所有好处。 具体请参考第二章的图2-1。 

dispatcherservlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:

1、文件上传解析,如果请求类型是multipart将通过multipartresolver进行文件上传解析;
2、通过handlermapping,将请求映射到处理器(返回一个handlerexecutionchain,它包括一个处理器、多个handlerinterceptor拦截器);
3、通过handleradapter支持多种类型的处理器(handlerexecutionchain中的处理器);
4、通过viewresolver解析逻辑视图名到具体视图实现;
5、本地化解析;
6、渲染具体的视图等;
7、如果执行过程中遇到异常将交给handlerexceptionresolver来解析。 

从以上我们可以看出dispatcherservlet主要负责流程的控制(而且在流程中的每个关键点都是很容易扩展的)。 

dispatcherservlet在web.xml中的配置

  <servlet>
    <servlet-name>chapter2</servlet-name>
    <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>chapter2</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

load-on-startup:表示启动容器时初始化该servlet;

url-pattern:表示哪些请求交给spring web mvc处理, “/” 是用来定义默认servlet映射的。也可以如“*.html”表示拦截所有以html为扩展名的请求。 

该dispatcherservlet默认使用webapplicationcontext作为上下文,spring默认配置文件为“/web-inf/[servlet名字]-servlet.xml”。 

dispatcherservlet也可以配置自己的初始化参数,覆盖默认配置:

参数

描述

contextclass

实现webapplicationcontext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定, 默认使用xmlwebapplicationcontext。

contextconfiglocation

传给上下文实例(由contextclass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。

namespace

webapplicationcontext命名空间。默认值是[server-name]-servlet。

 因此我们可以通过添加初始化参数

 
  <servlet>
    <servlet-name>chapter2</servlet-name>
    <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
      <param-name>contextconfiglocation</param-name>
      <param-value>classpath:spring-servlet-config.xml</param-value>
    </init-param>
  </servlet>

 如果使用如上配置,spring web mvc框架将加载“classpath:spring-servlet-config.xml”来进行初始化上下文而不是“/web-inf/[servlet名字]-servlet.xml”。

上下文关系

集成web环境的通用配置:

<context-param>
   <param-name>contextconfiglocation</param-name>
   <param-value>
     classpath:spring-common-config.xml,
     classpath:spring-budget-config.xml
   </param-value>
</context-param>
<listener> <listener-class>org.springframework.web.context.contextloaderlistener</listener-class>
</listener>

如上配置是spring集成web环境的通用配置;一般用于加载除web层的bean(如dao、service等),以便于与其他任何web框架集成。

contextconfiglocation:表示用于加载bean的配置文件;

contextclass:表示用于加载bean的applicationcontext实现类,默认webapplicationcontext。 

创建完毕后会将该上下文放在servletcontext:

servletcontext.setattribute(
webapplicationcontext.root_web_application_context_attribute,
this.context);

contextloaderlistener初始化的上下文和dispatcherservlet初始化的上下文关系,如图3-1

Spring MVC之DispatcherServlet详解_动力节点Java学院整理

图3-1

从图中可以看出:
contextloaderlistener初始化的上下文加载的bean是对于整个应用程序共享的,不管是使用什么表现层技术,一般如dao层、service层bean;
dispatcherservlet初始化的上下文加载的bean是只对spring web mvc有效的bean,如controller、handlermapping、handleradapter等等,该初始化上下文应该只加载web相关组件。

dispatcherservlet初始化顺序

继承体系结构如下所示:

Spring MVC之DispatcherServlet详解_动力节点Java学院整理

 1、httpservletbean继承httpservlet,因此在web容器启动时将调用它的init方法,该初始化方法的主要作用
:::将servlet初始化参数(init-param)设置到该组件上(如contextattribute、contextclass、namespace、contextconfiglocation),通过beanwrapper简化设值过程,方便后续使用;
:::提供给子类初始化扩展点,initservletbean(),该方法由frameworkservlet覆盖。

public abstract class httpservletbean extends httpservlet implements environmentaware{
@override
  public final void init() throws servletexception {
    //省略部分代码
    //1、如下代码的作用是将servlet初始化参数设置到该组件上
//如contextattribute、contextclass、namespace、contextconfiglocation;
    try {
      propertyvalues pvs = new servletconfigpropertyvalues(getservletconfig(), this.requiredproperties);
      beanwrapper bw = propertyaccessorfactory.forbeanpropertyaccess(this);
      resourceloader resourceloader = new servletcontextresourceloader(getservletcontext());
      bw.registercustomeditor(resource.class, new resourceeditor(resourceloader, this.environment));
      initbeanwrapper(bw);
      bw.setpropertyvalues(pvs, true);
    }
    catch (beansexception ex) {
      //…………省略其他代码
    }
    //2、提供给子类初始化的扩展点,该方法由frameworkservlet覆盖
    initservletbean();
    if (logger.isdebugenabled()) {
      logger.debug("servlet '" + getservletname() + "' configured successfully");
    }
  }
  //…………省略其他代码
}
 

 frameworkservlet继承httpservletbean,通过initservletbean()进行web上下文初始化,该方法主要覆盖一下两件事情: 

   初始化web上下文;

    提供给子类初始化扩展点;

public abstract class frameworkservlet extends httpservletbean {
@override
  protected final void initservletbean() throws servletexception {
    //省略部分代码
    try {
       //1、初始化web上下文
      this.webapplicationcontext = initwebapplicationcontext();
       //2、提供给子类初始化的扩展点
      initframeworkservlet();
    }
    //省略部分代码
  }
}
 
protected webapplicationcontext initwebapplicationcontext() {
    //root上下文(contextloaderlistener加载的)
    webapplicationcontext rootcontext =
       webapplicationcontextutils.getwebapplicationcontext(getservletcontext());
    webapplicationcontext wac = null;
    if (this.webapplicationcontext != null) {
      // 1、在创建该servlet注入的上下文
      wac = this.webapplicationcontext;
      if (wac instanceof configurablewebapplicationcontext) {
       configurablewebapplicationcontext cwac = (configurablewebapplicationcontext) wac;
       if (!cwac.isactive()) {
         if (cwac.getparent() == null) {
           cwac.setparent(rootcontext);
         }
         configureandrefreshwebapplicationcontext(cwac);
       }
      }
    }
    if (wac == null) {
       //2、查找已经绑定的上下文
      wac = findwebapplicationcontext();
    }
    if (wac == null) {
      //3、如果没有找到相应的上下文,并指定父亲为contextloaderlistener
      wac = createwebapplicationcontext(rootcontext);
    }
    if (!this.refresheventreceived) {
       //4、刷新上下文(执行一些初始化)
      onrefresh(wac);
    }
    if (this.publishcontext) {
      // publish the context as a servlet context attribute.
      string attrname = getservletcontextattributename();
      getservletcontext().setattribute(attrname, wac);
      //省略部分代码
    }
    return wac;
  }
 

从initwebapplicationcontext()方法可以看出,基本上如果contextloaderlistener加载了上下文将作为根上下文(dispatcherservlet的父容器)。 

最后调用了onrefresh()方法执行容器的一些初始化,这个方法由子类实现,来进行扩展。  

dispatcherservlet继承frameworkservlet,并实现了onrefresh()方法提供一些前端控制器相关的配置: 

public class dispatcherservlet extends frameworkservlet {
   //实现子类的onrefresh()方法,该方法委托为initstrategies()方法。
  @override
  protected void onrefresh(applicationcontext context) {
    initstrategies(context);
  }
 
  //初始化默认的spring web mvc框架使用的策略(如handlermapping)
  protected void initstrategies(applicationcontext context) {
    initmultipartresolver(context);
    initlocaleresolver(context);
    initthemeresolver(context);
    inithandlermappings(context);
    inithandleradapters(context);
    inithandlerexceptionresolvers(context);
    initrequesttoviewnametranslator(context);
    initviewresolvers(context);
    initflashmapmanager(context);
  }
}
 

从如上代码可以看出,dispatcherservlet启动时会进行我们需要的web层bean的配置,如handlermapping、handleradapter等,而且如果我们没有配置,还会给我们提供默认的配置。 

从如上代码我们可以看出,整个dispatcherservlet初始化的过程和做了些什么事情,具体主要做了如下两件事情:

1、初始化spring web mvc使用的web上下文,并且可能指定父容器为(contextloaderlistener加载了根上下文);
2、初始化dispatcherservlet使用的策略,如handlermapping、handleradapter等。 

服务器启动时的日志分析(此处加上了contextloaderlistener从而启动root上下文容器): 

 信息: initializing spring root webapplicationcontext //由contextloaderlistener启动root上下文
 
2012-03-12 13:33:55 [main] info org.springframework.web.context.contextloader - root webapplicationcontext: initialization started
2012-03-12 13:33:55 [main] info org.springframework.web.context.support.xmlwebapplicationcontext - refreshing root webapplicationcontext: startup date [mon mar 12 13:33:55 cst 2012]; root of context hierarchy
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.defaultbeandefinitiondocumentreader - loading bean definitions
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.xmlbeandefinitionreader - loaded 0 bean definitions from location pattern [/web-inf/contextloaderlistener.xml]
2012-03-12 13:33:55 [main] debug org.springframework.web.context.support.xmlwebapplicationcontext - bean factory for root webapplicationcontext: org.springframework.beans.factory.support.defaultlistablebeanfactory@1c05ffd: defining beans []; root of factory hierarchy
2012-03-12 13:33:55 [main] debug org.springframework.web.context.support.xmlwebapplicationcontext - bean factory for root webapplicationcontext:
2012-03-12 13:33:55 [main] debug org.springframework.web.context.contextloader - published root webapplicationcontext as servletcontext attribute with name [org.springframework.web.context.webapplicationcontext.root] //将root上下文绑定到servletcontext
2012-03-12 13:33:55 [main] info org.springframework.web.context.contextloader - root webapplicationcontext: initialization completed in 438 ms //到此root上下文启动完毕
 
 2012-03-12 13:33:55 [main] debug org.springframework.web.servlet.dispatcherservlet - initializing servlet 'chapter2'
信息: initializing spring frameworkservlet 'chapter2' //开始初始化frameworkservlet对应的web上下文
2012-03-12 13:33:55 [main] info org.springframework.web.servlet.dispatcherservlet - frameworkservlet 'chapter2': initialization started
2012-03-12 13:33:55 [main] debug org.springframework.web.servlet.dispatcherservlet - servlet with name 'chapter2' will try to create custom webapplicationcontext context of class 'org.springframework.web.context.support.xmlwebapplicationcontext', using parent context [root webapplicationcontext: startup date [mon mar 12 13:33:55 cst 2012]; root of context hierarchy]
//此处使用root webapplicationcontext作为父容器。
2012-03-12 13:33:55 [main] info org.springframework.web.context.support.xmlwebapplicationcontext - refreshing webapplicationcontext for namespace 'chapter2-servlet': startup date [mon mar 12 13:33:55 cst 2012]; parent: root webapplicationcontext
2012-03-12 13:33:55 [main] info org.springframework.beans.factory.xml.xmlbeandefinitionreader - loading xml bean definitions from servletcontext resource [/web-inf/chapter2-servlet.xml]
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.defaultbeandefinitiondocumentreader - loading bean definitions
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.beandefinitionparserdelegate - neither xml 'id' nor 'name' specified - using generated bean name[org.springframework.web.servlet.handler.beannameurlhandlermapping#0] //我们配置的handlermapping
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.beandefinitionparserdelegate - neither xml 'id' nor 'name' specified - using generated bean name[org.springframework.web.servlet.mvc.simplecontrollerhandleradapter#0] //我们配置的handleradapter
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.beandefinitionparserdelegate - neither xml 'id' nor 'name' specified - using generated bean name [org.springframework.web.servlet.view.internalresourceviewresolver#0] //我们配置的viewresolver
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.beandefinitionparserdelegate - no xml 'id' specified - using '/hello' as bean name and [] as aliases 
//我们的处理器(helloworldcontroller)
2012-03-12 13:33:55 [main] debug org.springframework.beans.factory.xml.xmlbeandefinitionreader - loaded 4 bean definitions from location pattern [/web-inf/chapter2-servlet.xml]
2012-03-12 13:33:55 [main] debug org.springframework.web.context.support.xmlwebapplicationcontext - bean factory for webapplicationcontext for namespace 'chapter2-servlet': org.springframework.beans.factory.support.defaultlistablebeanfactory@1372656: defining beans [org.springframework.web.servlet.handler.beannameurlhandlermapping#0,org.springframework.web.servlet.mvc.simplecontrollerhandleradapter#0,org.springframework.web.servlet.view.internalresourceviewresolver#0,/hello]; parent: org.springframework.beans.factory.support.defaultlistablebeanfactory@1c05ffd
//到此容器注册的bean初始化完毕
 
2012-03-12 13:33:56 [main] debug org.springframework.web.servlet.dispatcherservlet - unable to locate multipartresolver with name 'multipartresolver': no multipart request handling provided
 
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - creating instance of bean 'org.springframework.web.servlet.i18n.acceptheaderlocaleresolver'
//默认的localeresolver注册
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - creating instance of bean 'org.springframework.web.servlet.theme.fixedthemeresolver'
//默认的themeresolver注册
 
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - returning cached instance of singleton bean 'org.springframework.web.servlet.handler.beannameurlhandlermapping#0'
//发现我们定义的handlermapping 不再使用默认的handlermapping。
 
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - returning cached instance of singleton bean 'org.springframework.web.servlet.mvc.simplecontrollerhandleradapter#0'
//发现我们定义的handleradapter 不再使用默认的handleradapter。
 
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - creating instance of bean 'org.springframework.web.servlet.mvc.annotation.annotationmethodhandlerexceptionresolver'
//异常处理解析器exceptionresolver
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - creating instance of bean 'org.springframework.web.servlet.mvc.annotation.annotationmethodhandlerexceptionresolver'
 
2012-03-12 13:33:56 [main] debug org.springframework.beans.factory.support.defaultlistablebeanfactory - returning cached instance of singleton bean 'org.springframework.web.servlet.view.internalresourceviewresolver#0'
 
2012-03-12 13:33:56 [main] debug org.springframework.web.servlet.dispatcherservlet - published webapplicationcontext of servlet 'chapter2' as servletcontext attribute with name [org.springframework.web.servlet.frameworkservlet.context.chapter2]
//绑定frameworkservlet初始化的web上下文到servletcontext
2012-03-12 13:33:56 [main] info org.springframework.web.servlet.dispatcherservlet - frameworkservlet 'chapter2': initialization completed in 297 ms
2012-03-12 13:33:56 [main] debug org.springframework.web.servlet.dispatcherservlet - servlet 'chapter2' configured successfully
//到此完整流程结束 
 

 从如上日志我们也可以看出,dispatcherservlet会进行一些默认的配置。接下来我们看一下默认配置吧。 

dispatcherservlet默认配置

dispatcherservlet的默认配置在dispatcherservlet.properties(和dispatcherservlet类在一个包下)中,而且是当spring配置文件中没有指定配置时使用的默认策略:

Spring MVC之DispatcherServlet详解_动力节点Java学院整理

 
org.springframework.web.servlet.localeresolver=org.springframework.web.servlet.i18n.acceptheaderlocaleresolver
 
org.springframework.web.servlet.themeresolver=org.springframework.web.servlet.theme.fixedthemeresolver
 
org.springframework.web.servlet.handlermapping=org.springframework.web.servlet.handler.beannameurlhandlermapping,\
  org.springframework.web.servlet.mvc.annotation.defaultannotationhandlermapping
 
org.springframework.web.servlet.handleradapter=org.springframework.web.servlet.mvc.httprequesthandleradapter,\
  org.springframework.web.servlet.mvc.simplecontrollerhandleradapter,\
  org.springframework.web.servlet.mvc.annotation.annotationmethodhandleradapter
 
org.springframework.web.servlet.handlerexceptionresolver=org.springframework.web.servlet.mvc.annotation.annotationmethodhandlerexceptionresolver,\
  org.springframework.web.servlet.mvc.annotation.responsestatusexceptionresolver,\
  org.springframework.web.servlet.mvc.support.defaulthandlerexceptionresolver
 
org.springframework.web.servlet.requesttoviewnametranslator=org.springframework.web.servlet.view.defaultrequesttoviewnametranslator
 
org.springframework.web.servlet.viewresolver=org.springframework.web.servlet.view.internalresourceviewresolver
 
org.springframework.web.servlet.flashmapmanager=org.springframework.web.servlet.support.sessionflashmapmanager
 
 

 从如上配置可以看出dispatcherservlet在启动时会自动注册这些特殊的bean,无需我们注册,如果我们注册了,默认的将不会注册。 

因此如第二章的beannameurlhandlermapping、simplecontrollerhandleradapter是不需要注册的,dispatcherservlet默认会注册这两个bean。 

从dispatcherservlet.properties可以看出有许多特殊的bean,那接下来我们就看看spring web mvc主要有哪些特殊的bean。

dispatcherservlet中使用的特殊的bean

dispatcherservlet默认使用webapplicationcontext作为上下文,因此我们来看一下该上下文中有哪些特殊的bean:

1、controller:处理器/页面控制器,做的是mvc中的c的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理;

2、handlermapping:请求到处理器的映射,如果映射成功返回一个handlerexecutionchain对象(包含一个handler处理器(页面控制器)对象、多个handlerinterceptor拦截器)对象;如beannameurlhandlermapping将url与bean名字映射,映射成功的bean就是此处的处理器;

3、handleradapter:handleradapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;如simplecontrollerhandleradapter将对实现了controller接口的bean进行适配,并且掉处理器的handlerequest方法进行功能处理;

4、viewresolver:viewresolver将把逻辑视图名解析为具体的view,通过这种策略模式,很容易更换其他视图技术;如internalresourceviewresolver将逻辑视图名映射为jsp视图;

5、localresover:本地化解析,因为spring支持国际化,因此localresover解析客户端的locale信息从而方便进行国际化;

6、themeresovler:主题解析,通过它来实现一个页面多套风格,即常见的类似于软件皮肤效果;

7、multipartresolver:文件上传解析,用于支持文件上传;

8、handlerexceptionresolver:处理器异常解析,可以将异常映射到相应的统一错误界面,从而显示用户友好的界面(而不是给用户看到具体的错误信息);

9、requesttoviewnametranslator:当处理器没有返回逻辑视图名等相关信息时,自动将请求url映射为逻辑视图名;

10、flashmapmanager:用于管理flashmap的策略接口,flashmap用于存储一个请求的输出,当进入另一个请求时作为该请求的输入,通常用于重定向场景,后边会细述。 

到此dispatcherservlet我们已经了解了,接下来我们就需要把上边提到的特殊bean挨个击破,那首先从控制器开始吧。