刚接触springmvc,对它的xml文件配置一直比较模模糊糊,最近花了一点时间稍微看了下源代码,再加上调试,开始逐渐理解它,网上的类似的内容有很多,写本文主要是自己加深一下理解。本文适合用过springmvc的开发者,言归正传,首先搭建一个最简单的工程体验一下。
该工程是基于maven的,pom配置不再说明,所使用的spring版本4.0.5。
首先是web.xml文件配置,最简单的配置
<a href="http://my.oschina.net/pingpangkuangmo/blog/376293#">?</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code><!doctype web-app public</code>
<code> </code><code>"-//sun microsystems, inc.//dtd web application 2.3//en"</code>
<code><web-app></code>
<code> </code><code><display-name>archetype created web application</display-name></code>
<code> </code><code><servlet></code>
<code> </code><code><servlet-name>mvc</servlet-name></code>
<code> </code><code><servlet-</code><code>class</code><code>>org.springframework.web.servlet.dispatcherservlet</servlet-</code><code>class</code><code>></code>
<code> </code><code><load-on-startup></code><code>1</code><code></load-on-startup></code>
<code> </code><code></servlet></code>
<code> </code><code><servlet-mapping></code>
<code> </code><code><url-pattern>/*</url-pattern></code>
<code> </code><code></servlet-mapping></code>
<code></web-app></code>
然后是mvc-servlet.xml文件的配置,上面配置dispatcherservlet会默认加载[servlet-name]-servlet.xml文件。对于我的配置,会去加载mvc-servlet.xml文件。
mvc-servlet.xml文件的内容:
18
19
20
21
22
23
24
25
26
27
28
29
<code><?xml version=</code><code>"1.0"</code> <code>encoding=</code><code>"utf-8"</code> <code>?></code>
<code> </code><code>xsi:schemalocation="http:</code><code>//www.springframework.org/schema/beans</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/beans/spring-beans-3.1.xsd</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/mvc</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/mvc/spring-mvc-3.1.xsd</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/util</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/util/spring-util-2.0.xsd</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/context</code>
<code> </code><code>http:</code><code>//www.springframework.org/schema/context/spring-context-3.2.xsd"></code>
<code> </code><code><bean name=</code><code>"/index"</code> <code>class</code><code>=</code><code>"com.lg.mvc.homeaction"</code><code>></bean></code>
<code> </code><code><bean</code><code>class</code><code>=</code><code>"org.springframework.web.servlet.view.freemarker.freemarkerconfigurer"</code><code>></code>
<code> </code><code><property name=</code><code>"templateloaderpath"</code> <code>value=</code><code>"/web-inf/views"</code> <code>/></code>
<code> </code><code><property name=</code><code>"defaultencoding"</code> <code>value=</code><code>"utf-8"</code> <code>/></code>
<code> </code><code><property name=</code><code>"freemarkersettings"</code><code>></code>
<code> </code><code><props></code>
<code> </code><code><prop key=</code><code>"locale"</code><code>>zh_cn</prop></code>
<code> </code><code></props></code>
<code> </code><code></property></code>
<code> </code><code></bean></code>
<code> </code><code><bean</code><code>class</code><code>=</code><code>"org.springframework.web.servlet.view.freemarker.freemarkerviewresolver"</code><code>></code>
<code> </code><code><property name=</code><code>"suffix"</code> <code>value=</code><code>".html"</code> <code>/></code>
<code> </code><code><property name=</code><code>"contenttype"</code> <code>value=</code><code>"text/html;charset=utf-8"</code> <code>/></code>
<code> </code><code><property name=</code><code>"requestcontextattribute"</code> <code>value=</code><code>"request"</code> <code>/></code>
<code> </code><code><property name=</code><code>"exposerequestattributes"</code> <code>value=</code><code>"true"</code> <code>/></code>
<code> </code><code><property name=</code><code>"exposesessionattributes"</code> <code>value=</code><code>"true"</code> <code>/></code>
<code></beans></code>
在该配置中定义了一个homeaction的bean。内容为:
<code>package</code> <code>com.lg.mvc;</code>
<code>import</code> <code>javax.servlet.http.httpservletrequest;</code>
<code>import</code> <code>javax.servlet.http.httpservletresponse;</code>
<code>import</code> <code>org.springframework.web.servlet.modelandview;</code>
<code>import</code> <code>org.springframework.web.servlet.mvc.controller;</code>
<code>public</code> <code>class</code> <code>homeaction</code><code>implements</code> <code>controller{</code>
<code> </code><code>@override</code>
<code> </code><code>public</code> <code>modelandview handlerequest(httpservletrequest request,</code>
<code> </code><code>httpservletresponse response)</code><code>throws</code> <code>exception {</code>
<code> </code><code>return</code> <code>new</code> <code>modelandview(</code><code>"hello"</code><code>);</code>
<code> </code><code>}</code>
<code>}</code>
这是最原始的mvc做法,要继承controller接口,先从原始的说起,最后再过渡到@controller和@requestmapping注解式的配置。它在mvc-serlet.xml文件中的配置有一个关键的属性name="/index"。
web-inf/view目录下有一个简单的hello.html,内容为:
<code><html></code>
<code> </code><code><head></code>
<code> </code>
<code> </code><code></head></code>
<code> </code><code><body></code>
<code> </code><code>hello lg !</code>
<code> </code><code></body></code>
<code></html></code>
至此该工程就写完了,部署到tomcat中,项目路径为/,运行一下。
访问 http://localhost:8080/index
至此整个工程就算搭建成功了。
下面就要说说原理了。
用过python django框架的都知道django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了。对于java的面向对象来说,就要分两步走。第一步首先要找到是哪个对象,即handler,本工程的handler则是homeaction对象。第二步要找到访问的函数,即homeaction的handlerequest方法。所以就出现了两个源码接口 handlermapping和handleradapter,前者负责第一步,后者负责第二步。借用网上的springmvc架构图。
handlermapping接口的实现(只举了我认识的几个) :
beannameurlhandlermapping :通过对比url和bean的name找到对应的对象
simpleurlhandlermapping :也是直接配置url和对应bean,比beannameurlhandlermapping功能更多
defaultannotationhandlermapping : 主要是针对注解配置@requestmapping的,已过时
requestmappinghandlermapping :取代了上面一个
handleradapter 接口实现:
httprequesthandleradapter : 要求handler实现httprequesthandler接口,该接口的方法为 void handlerequest(httpservletrequest request, httpservletresponse response)也就是 handler必须有一个handlerequest方法
simplecontrollerhandleradapter:要求handler实现controller接口,该接口的方法为modelandview handlerequest(httpservletrequest request, httpservletresponse response),也就是本工程采用的
annotationmethodhandleradapter :和上面的defaultannotationhandlermapping配对使用的,也已过时
requestmappinghandleradapter : 和上面的requestmappinghandlermapping配对使用,针对@requestmapping
先简单的说下这个工程的流程,访问http://localhost:8080/index首先由dispatcherservlet进行转发,通过beannameurlhandlermapping(含有 /index->homeaction的配置),找到了homeaction,然后再拿homeaction和每个adapter进行适配,由于homeaction实现了controller接口,所以最终会有simplecontrollerhandleradapter来完成对homeaction的handlerequest方法的调度。然后就顺利的执行了我们想要的方法,后面的内容不在本节中说明。
了解了大概流程,然后就需要看源代码了。
首先就是springmvc的入口类,dispatcherservlet,它实现了servlet接口,不再详细说dispatcherservlet的细节,不然又是一大堆的内容。每次请求都会调用它的doservice->dodispatch,我们关注的重点就在dodispatch方法中。
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<code>protected</code> <code>void</code> <code>dodispatch(httpservletrequest request, httpservletresponse response)</code><code>throws</code> <code>exception {</code>
<code> </code><code>httpservletrequest processedrequest = request;</code>
<code> </code><code>handlerexecutionchain mappedhandler =</code><code>null</code><code>;</code>
<code> </code><code>boolean</code> <code>multipartrequestparsed =</code><code>false</code><code>;</code>
<code> </code><code>webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request);</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>modelandview mv =</code><code>null</code><code>;</code>
<code> </code><code>exception dispatchexception =</code><code>null</code><code>;</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>processedrequest = checkmultipart(request);</code>
<code> </code><code>multipartrequestparsed = (processedrequest != request);</code>
<code> </code><code>//这个是重点,第一步由handlermapping找到对应的handler</code>
<code> </code><code>// determine handler for the current request.</code>
<code> </code><code>mappedhandler = gethandler(processedrequest);</code>
<code> </code><code>if</code> <code>(mappedhandler ==</code><code>null</code> <code>|| mappedhandler.gethandler() ==</code><code>null</code><code>) {</code>
<code> </code><code>nohandlerfound(processedrequest, response);</code>
<code> </code><code>return</code><code>;</code>
<code> </code><code>}</code>
<code> </code><code>// determine handler adapter for the current request.</code>
<code> </code><code>//这是第二步,找到合适的handleradapter,然后由它来调度执行handler的方法</code>
<code> </code><code>handleradapter ha = gethandleradapter(mappedhandler.gethandler());</code>
<code> </code><code>// process last-modified header, if supported by the handler.</code>
<code> </code><code>string method = request.getmethod();</code>
<code> </code><code>boolean</code> <code>isget =</code><code>"get"</code><code>.equals(method);</code>
<code> </code><code>if</code> <code>(isget ||</code><code>"head"</code><code>.equals(method)) {</code>
<code> </code><code>long</code> <code>lastmodified = ha.getlastmodified(request, mappedhandler.gethandler());</code>
<code> </code><code>if</code> <code>(logger.isdebugenabled()) {</code>
<code> </code><code>logger.debug(</code><code>"last-modified value for ["</code> <code>+ getrequesturi(request) +</code><code>"] is: "</code> <code>+ lastmodified);</code>
<code> </code><code>}</code>
<code> </code><code>if</code> <code>(</code><code>new</code> <code>servletwebrequest(request, response).checknotmodified(lastmodified) && isget) {</code>
<code> </code><code>return</code><code>;</code>
<code> </code><code>if</code> <code>(!mappedhandler.applyprehandle(processedrequest, response)) {</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>// actually invoke the handler.</code>
<code> </code><code>mv = ha.handle(processedrequest, response, mappedhandler.gethandler());</code>
<code> </code><code>finally</code> <code>{</code>
<code> </code><code>if</code> <code>(asyncmanager.isconcurrenthandlingstarted()) {</code>
<code> </code><code>applydefaultviewname(request, mv);</code>
<code> </code><code>mappedhandler.applyposthandle(processedrequest, response, mv);</code>
<code> </code><code>}</code>
<code> </code><code>catch</code> <code>(exception ex) {</code>
<code> </code><code>dispatchexception = ex;</code>
<code> </code><code>processdispatchresult(processedrequest, response, mappedhandler, mv, dispatchexception);</code>
<code> </code><code>}</code>
<code> </code><code>catch</code> <code>(exception ex) {</code>
<code> </code><code>triggeraftercompletion(processedrequest, response, mappedhandler, ex);</code>
<code> </code><code>catch</code> <code>(error err) {</code>
<code> </code><code>triggeraftercompletionwitherror(processedrequest, response, mappedhandler, err);</code>
<code> </code><code>finally</code> <code>{</code>
<code> </code><code>if</code> <code>(asyncmanager.isconcurrenthandlingstarted()) {</code>
<code> </code><code>// instead of posthandle and aftercompletion</code>
<code> </code><code>mappedhandler.applyafterconcurrenthandlingstarted(processedrequest, response);</code>
<code> </code><code>return</code><code>;</code>
<code> </code><code>// clean up any resources used by a multipart request.</code>
<code> </code><code>if</code> <code>(multipartrequestparsed) {</code>
<code> </code><code>cleanupmultipart(processedrequest);</code>
第一步详细查看:
<code>protected</code> <code>handlerexecutionchain gethandler(httpservletrequest request)</code><code>throws</code> <code>exception {</code>
<code> </code><code>for</code> <code>(handlermapping hm :</code><code>this</code><code>.handlermappings) {</code>
<code> </code><code>if</code> <code>(logger.istraceenabled()) {</code>
<code> </code><code>logger.trace(</code>
<code> </code><code>"testing handler map ["</code> <code>+ hm +</code><code>"] in dispatcherservlet with name '"</code> <code>+ getservletname() +</code><code>"'"</code><code>);</code>
<code> </code><code>handlerexecutionchain handler = hm.gethandler(request);</code>
<code> </code><code>if</code> <code>(handler !=</code><code>null</code><code>) {</code>
<code> </code><code>return</code> <code>handler;</code>
<code> </code><code>return</code> <code>null</code><code>;</code>
可以看到就是通过遍历所有已注册的handlermapping来找到对应的handler,然后构建出一个handlerexecutionchain,它包含了handler和handlermapping本身的一些拦截器,如下
<code>public</code> <code>class</code> <code>handlerexecutionchain {</code>
<code> </code><code>private</code> <code>final</code> <code>object handler;</code>
<code> </code><code>private</code> <code>handlerinterceptor[] interceptors;</code>
<code> </code><code>private</code> <code>list<handlerinterceptor> interceptorlist;</code>
<code> </code>
<code> </code><code>//其他代码省略</code>
其中handlermapping的gethandler实现:
<code>public</code> <code>final</code> <code>handlerexecutionchain gethandler(httpservletrequest request)</code><code>throws</code> <code>exception {</code>
<code> </code><code>object handler = gethandlerinternal(request);</code>
<code> </code><code>if</code> <code>(handler ==</code><code>null</code><code>) {</code>
<code> </code><code>handler = getdefaulthandler();</code>
<code> </code><code>return</code> <code>null</code><code>;</code>
<code> </code><code>// bean name or resolved handler?</code>
<code> </code><code>if</code> <code>(handler</code><code>instanceof</code> <code>string) {</code>
<code> </code><code>string handlername = (string) handler;</code>
<code> </code><code>handler = getapplicationcontext().getbean(handlername);</code>
<code> </code><code>return</code> <code>gethandlerexecutionchain(handler, request);</code>
这里的gethandlerinternal(request)是个抽象方法,由具体的handlermapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为string类型,则表示这个则会去spring容器里面去找这样名字的bean。
再看下beannameurlhandlermapping的gethandlerinternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到beannameurlhandlermapping这只用实现该方法中的一部分),如下
<code>public</code> <code>class</code> <code>beannameurlhandlermapping</code><code>extends</code> <code>abstractdetectingurlhandlermapping {</code>
<code> </code><code>/**</code>
<code> </code><code>* checks name and aliases of the given bean for urls, starting with "/".</code>
<code> </code><code>*/</code>
<code> </code><code>protected</code> <code>string[] determineurlsforhandler(string beanname) {</code>
<code> </code><code>list<string> urls =</code><code>new</code> <code>arraylist<string>();</code>
<code> </code><code>if</code> <code>(beanname.startswith(</code><code>"/"</code><code>)) {</code>
<code> </code><code>urls.add(beanname);</code>
<code> </code><code>string[] aliases = getapplicationcontext().getaliases(beanname);</code>
<code> </code><code>for</code> <code>(string alias : aliases) {</code>
<code> </code><code>if</code> <code>(alias.startswith(</code><code>"/"</code><code>)) {</code>
<code> </code><code>urls.add(alias);</code>
<code> </code><code>return</code> <code>stringutils.tostringarray(urls);</code>
这里面注释说,bean的name必须以/开头,它才处理,将信息存储在map<string, object> handlermap中,对于本工程来说就是{'/index':homeaction对象}。
至此这里完成了第一步,下面开始第二步,即方法handleradapter ha = gethandleradapter(mappedhandler.gethandler());的具体实现:
<code>protected</code> <code>handleradapter gethandleradapter(object handler)</code><code>throws</code> <code>servletexception {</code>
<code> </code><code>for</code> <code>(handleradapter ha :</code><code>this</code><code>.handleradapters) {</code>
<code> </code><code>logger.trace(</code><code>"testing handler adapter ["</code> <code>+ ha +</code><code>"]"</code><code>);</code>
<code> </code><code>if</code> <code>(ha.supports(handler)) {</code>
<code> </code><code>return</code> <code>ha;</code>
<code> </code><code>throw</code> <code>new</code> <code>servletexception(</code><code>"no adapter for handler ["</code> <code>+ handler +</code>
<code> </code><code>"]: the dispatcherservlet configuration needs to include a handleradapter that supports this handler"</code><code>);</code>
遍历所有的handleradapter,判断他们是否支持这个handler。
我们来看下httprequesthandleradapter的supports(handler)方法:
<code>public</code> <code>class</code> <code>httprequesthandleradapter</code><code>implements</code> <code>handleradapter {</code>
<code> </code><code>public</code> <code>boolean</code> <code>supports(object handler) {</code>
<code> </code><code>//就是判断handler是否实现了httprequesthandler接口</code>
<code> </code><code>return</code> <code>(handler</code><code>instanceof</code> <code>httprequesthandler);</code>
<code> </code><code>public</code> <code>modelandview handle(httpservletrequest request, httpservletresponse response, object handler)</code>
<code> </code><code>throws</code> <code>exception {</code>
<code> </code><code>//若handler实现了httprequesthandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑</code>
<code> </code><code>((httprequesthandler) handler).handlerequest(request, response);</code>
<code> </code><code>public</code> <code>long</code> <code>getlastmodified(httpservletrequest request, object handler) {</code>
<code> </code><code>if</code> <code>(handler</code><code>instanceof</code> <code>lastmodified) {</code>
<code> </code><code>return</code> <code>((lastmodified) handler).getlastmodified(request);</code>
<code> </code><code>return</code> <code>-1l;</code>
同理simplecontrollerhandleradapter也是这样类似的逻辑
<code>public</code> <code>class</code> <code>simplecontrollerhandleradapter</code><code>implements</code> <code>handleradapter {</code>
<code> </code><code>return</code> <code>(handler</code><code>instanceof</code> <code>controller);</code>
<code> </code><code>return</code> <code>((controller) handler).handlerequest(request, response);</code>
剩余两个annotationmethodhandleradapter和requestmappinghandleradapter就比较复杂,我也没看。
按照本工程的配置,则simplecontrollerhandleradapter是支持homeaction的,然后就会执行simplecontrollerhandleradapter的handle(processedrequest, response, mappedhandler.gethandler())方法。本质上就会调用homeaction实现controller接口的方法。至此就分析完了。
了解过程了之后,然后就是最重要的也是经常配置出问题的地方。dispatcherservlet的handlermappings和handleradapters的来源问题。
dispatcherservlet初始化的时候,会调用一个方法如下:
<code>protected</code> <code>void</code> <code>initstrategies(applicationcontext context) {</code>
<code> </code><code>initmultipartresolver(context);</code>
<code> </code><code>initlocaleresolver(context);</code>
<code> </code><code>initthemeresolver(context);</code>
<code>//初始化一些handlermapping</code>
<code> </code><code>inithandlermappings(context);</code>
<code>//初始化一些handleradapter</code>
<code> </code><code>inithandleradapters(context);</code>
<code> </code><code>inithandlerexceptionresolvers(context);</code>
<code> </code><code>initrequesttoviewnametranslator(context);</code>
<code> </code><code>initviewresolvers(context);</code>
<code> </code><code>initflashmapmanager(context);</code>
这里可以看到,它会初始化一些handlermapping和handleradapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法:
<code>private</code> <code>void</code> <code>inithandlermappings(applicationcontext context) {</code>
<code> </code><code>this</code><code>.handlermappings =</code><code>null</code><code>;</code>
<code> </code><code>if</code> <code>(</code><code>this</code><code>.detectallhandlermappings) {</code>
<code> </code><code>// find all handlermappings in the applicationcontext, including ancestor contexts.</code>
<code> </code><code>map<string, handlermapping> matchingbeans =</code>
<code> </code><code>beanfactoryutils.beansoftypeincludingancestors(context, handlermapping.</code><code>class</code><code>,</code><code>true</code><code>,</code><code>false</code><code>);</code>
<code> </code><code>if</code> <code>(!matchingbeans.isempty()) {</code>
<code> </code><code>this</code><code>.handlermappings =</code><code>new</code> <code>arraylist<handlermapping>(matchingbeans.values());</code>
<code> </code><code>// we keep handlermappings in sorted order.</code>
<code> </code><code>ordercomparator.sort(</code><code>this</code><code>.handlermappings);</code>
<code> </code><code>else</code> <code>{</code>
<code> </code><code>handlermapping hm = context.getbean(handler_mapping_bean_name, handlermapping.</code><code>class</code><code>);</code>
<code> </code><code>this</code><code>.handlermappings = collections.singletonlist(hm);</code>
<code> </code><code>catch</code> <code>(nosuchbeandefinitionexception ex) {</code>
<code> </code><code>// ignore, we'll add a default handlermapping later.</code>
<code> </code><code>// ensure we have at least one handlermapping, by registering</code>
<code> </code><code>// a default handlermapping if no other mappings are found.</code>
<code> </code><code>if</code> <code>(</code><code>this</code><code>.handlermappings ==</code><code>null</code><code>) {</code>
<code> </code><code>this</code><code>.handlermappings = getdefaultstrategies(context, handlermapping.</code><code>class</code><code>);</code>
<code> </code><code>if</code> <code>(logger.isdebugenabled()) {</code>
<code> </code><code>logger.debug(</code><code>"no handlermappings found in servlet '"</code> <code>+ getservletname() +</code><code>"': using default"</code><code>);</code>
detectallhandlermappings是dispatcherservlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了handlermapping的bean,如果有,则加入dispatcherservlet的handlermappings中。如果detectallhandlermappings为false,则直接去容器中找id="handlermapping"且实现了handlermapping的bean.如果以上都没找到,则会去加载默认的handlermapping。
<code>/** detect all handlermappings or just expect "handlermapping" bean? */</code>
<code> </code><code>private</code> <code>boolean</code> <code>detectallhandlermappings =</code><code>true</code><code>;</code>
本工程由于没有配置handlermapping,所以它会去加载默认的,下面看看默认的配置是什么
<code>protected</code> <code><t> list<t> getdefaultstrategies(applicationcontext context, class<t> strategyinterface) {</code>
<code> </code><code>string key = strategyinterface.getname();</code>
<code>//defaultstrategies存储了默认的配置</code>
<code> </code><code>string value = defaultstrategies.getproperty(key);</code>
<code> </code><code>if</code> <code>(value !=</code><code>null</code><code>) {</code>
<code> </code><code>string[] classnames = stringutils.commadelimitedlisttostringarray(value);</code>
<code> </code><code>list<t> strategies =</code><code>new</code> <code>arraylist<t>(classnames.length);</code>
<code> </code><code>for</code> <code>(string classname : classnames) {</code>
<code> </code><code>class<?> clazz = classutils.forname(classname, dispatcherservlet.</code><code>class</code><code>.getclassloader());</code>
<code> </code><code>object strategy = createdefaultstrategy(context, clazz);</code>
<code> </code><code>strategies.add((t) strategy);</code>
<code> </code><code>catch</code> <code>(classnotfoundexception ex) {</code>
<code> </code><code>throw</code> <code>new</code> <code>beaninitializationexception(</code>
<code> </code><code>"could not find dispatcherservlet's default strategy class ["</code> <code>+ classname +</code>
<code> </code><code>"] for interface ["</code> <code>+ key +</code><code>"]"</code><code>, ex);</code>
<code> </code><code>catch</code> <code>(linkageerror err) {</code>
<code> </code><code>"error loading dispatcherservlet's default strategy class ["</code> <code>+ classname +</code>
<code> </code><code>"] for interface ["</code> <code>+ key +</code><code>"]: problem with class file or dependent class"</code><code>, err);</code>
<code> </code><code>return</code> <code>strategies;</code>
<code> </code><code>return</code> <code>new</code> <code>linkedlist<t>();</code>
继续看看defaultstrategies是如何初始化的:
<code>private</code> <code>static</code> <code>final</code> <code>properties defaultstrategies;</code>
<code> </code><code>static</code> <code>{</code>
<code> </code><code>// load default strategy implementations from properties file.</code>
<code> </code><code>// this is currently strictly internal and not meant to be customized</code>
<code> </code><code>// by application developers.</code>
<code>//这里的default_strategies_path就是dispatcherservlet.properties</code>
<code> </code><code>classpathresource resource =</code><code>new</code> <code>classpathresource(default_strategies_path, dispatcherservlet.</code><code>class</code><code>);</code>
<code> </code><code>defaultstrategies = propertiesloaderutils.loadproperties(resource);</code>
<code> </code><code>catch</code> <code>(ioexception ex) {</code>
<code> </code><code>throw</code> <code>new</code> <code>illegalstateexception(</code><code>"could not load 'dispatcherservlet.properties': "</code> <code>+ ex.getmessage());</code>
这里使用静态代码块来加载配置文件dispatcherservlet.properties,它所在位置就是和dispatcherservlet同一目录下面的,如下图所示:
该默认的配置文件的内容如下:
<code># default implementation classes</code><code>for</code> <code>dispatcherservlet's strategy interfaces.</code>
<code># used as fallback when no matching beans are found in the dispatcherservlet context.</code>
<code># not meant to be customized by application developers.</code>
<code>org.springframework.web.servlet.localeresolver=org.springframework.web.servlet.i18n.acceptheaderlocaleresolver</code>
<code>org.springframework.web.servlet.themeresolver=org.springframework.web.servlet.theme.fixedthemeresolver</code>
<code>#这里就是默认的handlermapping的配置</code>
<code>org.springframework.web.servlet.handlermapping=org.springframework.web.servlet.handler.beannameurlhandlermapping,\</code>
<code> </code><code>org.springframework.web.servlet.mvc.annotation.defaultannotationhandlermapping</code>
<code>#这里就是默认的handleradapter的配置</code>
<code>org.springframework.web.servlet.handleradapter=org.springframework.web.servlet.mvc.httprequesthandleradapter,\</code>
<code> </code><code>org.springframework.web.servlet.mvc.simplecontrollerhandleradapter,\</code>
<code> </code><code>org.springframework.web.servlet.mvc.annotation.annotationmethodhandleradapter</code>
<code>org.springframework.web.servlet.handlerexceptionresolver=org.springframework.web.servlet.mvc.annotation.annotationmethodhandlerexceptionresolver,\</code>
<code> </code><code>org.springframework.web.servlet.mvc.annotation.responsestatusexceptionresolver,\</code>
<code> </code><code>org.springframework.web.servlet.mvc.support.defaulthandlerexceptionresolver</code>
<code>org.springframework.web.servlet.requesttoviewnametranslator=org.springframework.web.servlet.view.defaultrequesttoviewnametranslator</code>
<code>org.springframework.web.servlet.viewresolver=org.springframework.web.servlet.view.internalresourceviewresolver</code>
<code>org.springframework.web.servlet.flashmapmanager=org.springframework.web.servlet.support.sessionflashmapmanager</code>
也就是说,当你什么都没有配置时,默认会加载以上的配置。正是由于有了上述默认配置的beannameurlhandlermapping(它要求name必须是以/开头的),它才会存储我们在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.homeaction"></bean>,同样正是由于有了simplecontrollerhandleradapter(由于handler实现了controller接口,所以它的support方法支持我们的handler),才会调度执行homeaction的handlerequest方法。