Spring MVC之DispatcherServlet详解_动力节点Java学院整理
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
图3-1
从图中可以看出:
contextloaderlistener初始化的上下文加载的bean是对于整个应用程序共享的,不管是使用什么表现层技术,一般如dao层、service层bean;
dispatcherservlet初始化的上下文加载的bean是只对spring web mvc有效的bean,如controller、handlermapping、handleradapter等等,该初始化上下文应该只加载web相关组件。
dispatcherservlet初始化顺序
继承体系结构如下所示:
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配置文件中没有指定配置时使用的默认策略:
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挨个击破,那首先从控制器开始吧。
推荐阅读
-
Struts1之url截取_动力节点Java学院整理
-
JavaScript之浏览器对象_动力节点Java学院整理
-
JavaScript之map reduce_动力节点Java学院整理
-
JavaScript之Map和Set_动力节点Java学院整理
-
JavaScript之filter_动力节点Java学院整理
-
JavaScript之Date_动力节点Java学院整理
-
underscore之function_动力节点Java学院整理
-
underscore之Collections_动力节点Java学院整理
-
JavaScript之Canvas_动力节点Java学院整理
-
underscore之Chaining_动力节点Java学院整理