刚接触SpringMVC,对它的xml文件配置一直比较模模糊糊,最近花了一点时间稍微看了下源代码,再加上调试,开始逐渐理解它,网上的类似的内容有很多,写本文主要是自己加深一下理解。本文适合用过SpringMVC的开发者,言归正传,首先搭建一个最简单的工程体验一下。
该工程是基于maven的,pom配置不再说明,所使用的spring版本4.0.5。
首先是web.xml文件配置,最简单的配置
然后是mvc-servlet.xml文件的配置,上面配置DispatcherServlet会默认加载[servlet-name]-servlet.xml文件。对于我的配置,会去加载mvc-servlet.xml文件。
mvc-servlet.xml文件的内容:
在该配置中定义了一个HomeAction的Bean。内容为:
这是最原始的mvc做法,要继承Controller接口,先从原始的说起,最后再过渡到@Controller和@RequestMapping注解式的配置。它在mvc-serlet.xml文件中的配置有一个关键的属性name="/index"。
WEB-INF/view目录下有一个简单的hello.html,内容为:
至此该工程就写完了,部署到tomcat中,项目路径为/,运行一下。
访问 http://localhost:8080/index
至此整个工程就算搭建成功了。
下面就要说说原理了。
用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了。对于java的面向对象来说,就要分两步走。第一步首先要找到是哪个对象,即handler,本工程的handler则是HomeAction对象。第二步要找到访问的函数,即HomeAction的handleRequest方法。所以就出现了两个源码接口 HandlerMapping和HandlerAdapter,前者负责第一步,后者负责第二步。借用网上的SpringMVC架构图。
HandlerMapping接口的实现(只举了我认识的几个) :
HandlerAdapter 接口实现:
先简单的说下这个工程的流程,访问http://localhost:8080/index首先由DispatcherServlet进行转发,通过BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然后再拿HomeAction和每个adapter进行适配,由于HomeAction实现了Controller接口,所以最终会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest方法的调度。然后就顺利的执行了我们想要的方法,后面的内容不在本节中说明。
了解了大概流程,然后就需要看源代码了。
首先就是SpringMVC的入口类,DispatcherServlet,它实现了Servlet接口,不再详细说DispatcherServlet的细节,不然又是一大堆的内容。每次请求都会调用它的doService->doDispatch,我们关注的重点就在doDispatch方法中。
第一步详细查看:
可以看到就是通过遍历所有已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些拦截器,如下
其中HandlerMapping的getHandler实现:
这里的getHandlerInternal(request)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只用实现该方法中的一部分),如下
这里面注释说,bean的name必须以/开头,它才处理,将信息存储在Map<String, Object> handlerMap中,对于本工程来说就是{'/index':HomeAction对象}。
至此这里完成了第一步,下面开始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具体实现:
遍历所有的HandlerAdapter,判断他们是否支持这个handler。
我们来看下HttpRequestHandlerAdapter的supports(handler)方法:
同理SimpleControllerHandlerAdapter也是这样类似的逻辑
剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。
按照本工程的配置,则SimpleControllerHandlerAdapter是支持HomeAction的,然后就会执行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本质上就会调用HomeAction实现Controller接口的方法。至此就分析完了。
了解过程了之后,然后就是最重要的也是经常配置出问题的地方。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。
DispatcherServlet初始化的时候,会调用一个方法如下:
这里可以看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法:
detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。
本工程由于没有配置HandlerMapping,所以它会去加载默认的,下面看看默认的配置是什么
继续看看defaultStrategies是如何初始化的:
这里使用静态代码块来加载配置文件DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目录下面的,如下图所示:
该默认的配置文件的内容如下 :
也就是说,当你什么都没有配置时,默认会加载以上的配置。正是由于有了上述默认配置的BeanNameUrlHandlerMapping(它要求name必须是以/开头的),它才会存储我们在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.HomeAction"></bean>,同样正是由于有了SimpleControllerHandlerAdapter(由于handler实现了Controller接口,所以它的support方法支持我们的handler),才会调度执行HomeAction的handleRequest方法。
该工程是基于maven的,pom配置不再说明,所使用的spring版本4.0.5。
首先是web.xml文件配置,最简单的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-
class
>org.springframework.web.servlet.DispatcherServlet</servlet-
class
>
<load-on-startup>
1
</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
|
然后是mvc-servlet.xml文件的配置,上面配置DispatcherServlet会默认加载[servlet-name]-servlet.xml文件。对于我的配置,会去加载mvc-servlet.xml文件。
mvc-servlet.xml文件的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<beans xmlns=
"http://www.springframework.org/schema/beans"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc=
"http://www.springframework.org/schema/mvc"
xmlns:util=
"http://www.springframework.org/schema/util"
xmlns:context=
"http://www.springframework.org/schema/context"
xsi:schemaLocation="http:
//www.springframework.org/schema/beans
http:
//www.springframework.org/schema/beans/spring-beans-3.1.xsd
http:
//www.springframework.org/schema/mvc
http:
//www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http:
//www.springframework.org/schema/util
http:
//www.springframework.org/schema/util/spring-util-2.0.xsd
http:
//www.springframework.org/schema/context
http:
//www.springframework.org/schema/context/spring-context-3.2.xsd">
<bean name=
"/index"
class
=
"com.lg.mvc.HomeAction"
></bean>
<bean
class
=
"org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
>
<property name=
"templateLoaderPath"
value=
"/WEB-INF/views"
/>
<property name=
"defaultEncoding"
value=
"utf-8"
/>
<property name=
"freemarkerSettings"
>
<props>
<prop key=
"locale"
>zh_CN</prop>
</props>
</property>
</bean>
<bean
class
=
"org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
>
<property name=
"suffix"
value=
".html"
/>
<property name=
"contentType"
value=
"text/html;charset=utf-8"
/>
<property name=
"requestContextAttribute"
value=
"request"
/>
<property name=
"exposeRequestAttributes"
value=
"true"
/>
<property name=
"exposeSessionAttributes"
value=
"true"
/>
</bean>
</beans>
|
在该配置中定义了一个HomeAction的Bean。内容为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package
com.lg.mvc;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.springframework.web.servlet.ModelAndView;
import
org.springframework.web.servlet.mvc.Controller;
public
class
HomeAction
implements
Controller{
@Override
public
ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws
Exception {
return
new
ModelAndView(
"hello"
);
}
}
|
这是最原始的mvc做法,要继承Controller接口,先从原始的说起,最后再过渡到@Controller和@RequestMapping注解式的配置。它在mvc-serlet.xml文件中的配置有一个关键的属性name="/index"。
WEB-INF/view目录下有一个简单的hello.html,内容为:
1
2
3
4
5
6
7
8
|
<html>
<head>
</head>
<body>
hello lg !
</body>
</html>
|
至此该工程就写完了,部署到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方法中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
|
protected
void
doDispatch(HttpServletRequest request, HttpServletResponse response)
throws
Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler =
null
;
boolean
multipartRequestParsed =
false
;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try
{
ModelAndView mv =
null
;
Exception dispatchException =
null
;
try
{
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//这个是重点,第一步由HandlerMapping找到对应的handler
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if
(mappedHandler ==
null
|| mappedHandler.getHandler() ==
null
) {
noHandlerFound(processedRequest, response);
return
;
}
// Determine handler adapter for the current request.
//这是第二步,找到合适的HandlerAdapter,然后由它来调度执行handler的方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean
isGet =
"GET"
.equals(method);
if
(isGet ||
"HEAD"
.equals(method)) {
long
lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if
(logger.isDebugEnabled()) {
logger.debug(
"Last-Modified value for ["
+ getRequestUri(request) +
"] is: "
+ lastModified);
}
if
(
new
ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return
;
}
}
if
(!mappedHandler.applyPreHandle(processedRequest, response)) {
return
;
}
try
{
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally
{
if
(asyncManager.isConcurrentHandlingStarted()) {
return
;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch
(Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch
(Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch
(Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally
{
if
(asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return
;
}
// Clean up any resources used by a multipart request.
if
(multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
|
第一步详细查看:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
protected
HandlerExecutionChain getHandler(HttpServletRequest request)
throws
Exception {
for
(HandlerMapping hm :
this
.handlerMappings) {
if
(logger.isTraceEnabled()) {
logger.trace(
"Testing handler map ["
+ hm +
"] in DispatcherServlet with name '"
+ getServletName() +
"'"
);
}
HandlerExecutionChain handler = hm.getHandler(request);
if
(handler !=
null
) {
return
handler;
}
}
return
null
;
}
|
可以看到就是通过遍历所有已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些拦截器,如下
1
2
3
4
5
6
7
8
9
10
|
public
class
HandlerExecutionChain {
private
final
Object handler;
private
HandlerInterceptor[] interceptors;
private
List<HandlerInterceptor> interceptorList;
//其他代码省略
}
|
其中HandlerMapping的getHandler实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
final
HandlerExecutionChain getHandler(HttpServletRequest request)
throws
Exception {
Object handler = getHandlerInternal(request);
if
(handler ==
null
) {
handler = getDefaultHandler();
}
if
(handler ==
null
) {
return
null
;
}
// Bean name or resolved handler?
if
(handler
instanceof
String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
return
getHandlerExecutionChain(handler, request);
}
|
这里的getHandlerInternal(request)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只用实现该方法中的一部分),如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
BeanNameUrlHandlerMapping
extends
AbstractDetectingUrlHandlerMapping {
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
protected
String[] determineUrlsForHandler(String beanName) {
List<String> urls =
new
ArrayList<String>();
if
(beanName.startsWith(
"/"
)) {
urls.add(beanName);
}
String[] aliases = getApplicationContext().getAliases(beanName);
for
(String alias : aliases) {
if
(alias.startsWith(
"/"
)) {
urls.add(alias);
}
}
return
StringUtils.toStringArray(urls);
}
}
|
这里面注释说,bean的name必须以/开头,它才处理,将信息存储在Map<String, Object> handlerMap中,对于本工程来说就是{'/index':HomeAction对象}。
至此这里完成了第一步,下面开始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具体实现:
1
2
3
4
5
6
7
8
9
10
11
12
|
protected
HandlerAdapter getHandlerAdapter(Object handler)
throws
ServletException {
for
(HandlerAdapter ha :
this
.handlerAdapters) {
if
(logger.isTraceEnabled()) {
logger.trace(
"Testing handler adapter ["
+ ha +
"]"
);
}
if
(ha.supports(handler)) {
return
ha;
}
}
throw
new
ServletException(
"No adapter for handler ["
+ handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"
);
}
|
遍历所有的HandlerAdapter,判断他们是否支持这个handler。
我们来看下HttpRequestHandlerAdapter的supports(handler)方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public
class
HttpRequestHandlerAdapter
implements
HandlerAdapter {
@Override
public
boolean
supports(Object handler) {
//就是判断handler是否实现了HttpRequestHandler接口
return
(handler
instanceof
HttpRequestHandler);
}
@Override
public
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception {
//若handler实现了HttpRequestHandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑
((HttpRequestHandler) handler).handleRequest(request, response);
return
null
;
}
@Override
public
long
getLastModified(HttpServletRequest request, Object handler) {
if
(handler
instanceof
LastModified) {
return
((LastModified) handler).getLastModified(request);
}
return
-1L;
}
}
|
同理SimpleControllerHandlerAdapter也是这样类似的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
class
SimpleControllerHandlerAdapter
implements
HandlerAdapter {
@Override
public
boolean
supports(Object handler) {
return
(handler
instanceof
Controller);
}
@Override
public
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception {
return
((Controller) handler).handleRequest(request, response);
}
@Override
public
long
getLastModified(HttpServletRequest request, Object handler) {
if
(handler
instanceof
LastModified) {
return
((LastModified) handler).getLastModified(request);
}
return
-1L;
}
}
|
剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。
按照本工程的配置,则SimpleControllerHandlerAdapter是支持HomeAction的,然后就会执行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本质上就会调用HomeAction实现Controller接口的方法。至此就分析完了。
了解过程了之后,然后就是最重要的也是经常配置出问题的地方。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。
DispatcherServlet初始化的时候,会调用一个方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
protected
void
initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//初始化一些HandlerMapping
initHandlerMappings(context);
//初始化一些HandlerAdapter
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
|
这里可以看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
private
void
initHandlerMappings(ApplicationContext context) {
this
.handlerMappings =
null
;
if
(
this
.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.
class
,
true
,
false
);
if
(!matchingBeans.isEmpty()) {
this
.handlerMappings =
new
ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(
this
.handlerMappings);
}
}
else
{
try
{
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.
class
);
this
.handlerMappings = Collections.singletonList(hm);
}
catch
(NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if
(
this
.handlerMappings ==
null
) {
this
.handlerMappings = getDefaultStrategies(context, HandlerMapping.
class
);
if
(logger.isDebugEnabled()) {
logger.debug(
"No HandlerMappings found in servlet '"
+ getServletName() +
"': using default"
);
}
}
}
|
detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。
1
2
|
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
private
boolean
detectAllHandlerMappings =
true
;
|
本工程由于没有配置HandlerMapping,所以它会去加载默认的,下面看看默认的配置是什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
protected
<T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
//defaultStrategies存储了默认的配置
String value = defaultStrategies.getProperty(key);
if
(value !=
null
) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies =
new
ArrayList<T>(classNames.length);
for
(String className : classNames) {
try
{
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.
class
.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch
(ClassNotFoundException ex) {
throw
new
BeanInitializationException(
"Could not find DispatcherServlet's default strategy class ["
+ className +
"] for interface ["
+ key +
"]"
, ex);
}
catch
(LinkageError err) {
throw
new
BeanInitializationException(
"Error loading DispatcherServlet's default strategy class ["
+ className +
"] for interface ["
+ key +
"]: problem with class file or dependent class"
, err);
}
}
return
strategies;
}
else
{
return
new
LinkedList<T>();
}
}
|
继续看看defaultStrategies是如何初始化的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private
static
final
Properties defaultStrategies;
static
{
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try
{
//这里的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties
ClassPathResource resource =
new
ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.
class
);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch
(IOException ex) {
throw
new
IllegalStateException(
"Could not load 'DispatcherServlet.properties': "
+ ex.getMessage());
}
}
|
这里使用静态代码块来加载配置文件DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目录下面的,如下图所示:
该默认的配置文件的内容如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# Default implementation classes
for
DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
#这里就是默认的HandlerMapping的配置
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
#这里就是默认的HandlerAdapter的配置
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
|
也就是说,当你什么都没有配置时,默认会加载以上的配置。正是由于有了上述默认配置的BeanNameUrlHandlerMapping(它要求name必须是以/开头的),它才会存储我们在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.HomeAction"></bean>,同样正是由于有了SimpleControllerHandlerAdapter(由于handler实现了Controller接口,所以它的support方法支持我们的handler),才会调度执行HomeAction的handleRequest方法。