HandlerMapping是用来找到url对应的处理handler对象(beans),而不是找到url对应的处理函数。Handleradapter则是用来匹配到handler的某个具体的处理函数上,然后调度执行这个函数。
ViewResolver:这个是用来解析视图,并返回资源到前端用到。
1. import
2. import
3.
4. import
5. import
6.
7. public class HomeAction implements
8.
9. @Override
10. public
11. throws
12. return new ModelAndView("hello");
13. }
14. }
下面就要说说原理了。
用过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. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws
2. HttpServletRequest processedRequest = request;
3. null;
4. boolean multipartRequestParsed = false;
5.
6. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
7.
8. try
9. null;
10. null;
11.
12. try
13. processedRequest = checkMultipart(request);
14. multipartRequestParsed = (processedRequest != request);
15. //这个是重点,第一步由HandlerMapping找到对应的handler
16. // Determine handler for the current request.
17. mappedHandler = getHandler(processedRequest);
18. if (mappedHandler == null || mappedHandler.getHandler() == null) {
19. noHandlerFound(processedRequest, response);
20. return;
21. }
22.
23. // Determine handler adapter for the current request.
24. //这是第二步,找到合适的HandlerAdapter,然后由它来调度执行handler的方法
25. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
26.
27. // Process last-modified header, if supported by the handler.
28. String method = request.getMethod();
29. boolean isGet = "GET".equals(method);
30. if (isGet || "HEAD".equals(method)) {
31. long
32. if
33. "Last-Modified value for [" + getRequestUri(request) + "] is: "
34. }
35. if (new
36. return;
37. }
38. }
39.
40. if
41. return;
42. }
43.
44. try
45. // Actually invoke the handler.
46. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
47. }
48. finally
49. if
50. return;
51. }
52. }
53.
54. applyDefaultViewName(request, mv);
55. mappedHandler.applyPostHandle(processedRequest, response, mv);
56. }
57. catch
58. dispatchException = ex;
59. }
60. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
61. }
62. catch
63. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
64. }
65. catch
66. triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
67. }
68. finally
69. if
70. // Instead of postHandle and afterCompletion
71. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
72. return;
73. }
74. // Clean up any resources used by a multipart request.
75. if
76. cleanupMultipart(processedRequest);
77. }
78. }
79. }
第一步详细查看:
1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws
2. for (HandlerMapping hm : this.handlerMappings) {
3. if
4. logger.trace(
5. "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
6. }
7. HandlerExecutionChain handler = hm.getHandler(request);
8. if (handler != null) {
9. return
10. }
11. }
12. return null;
13. }
可以看到就是通过遍历所有已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些拦截器,如下
1. public class
2.
3. private final
4.
5. private
6.
7. private
8.
9. //其他代码省略
10. }
其中HandlerMapping的getHandler实现:
1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws
2. Object handler = getHandlerInternal(request);
3. if (handler == null) {
4. handler = getDefaultHandler();
5. }
6. if (handler == null) {
7. return null;
8. }
9. // Bean name or resolved handler?
10. if (handler instanceof
11. String handlerName = (String) handler;
12. handler = getApplicationContext().getBean(handlerName);
13. }
14. return
15. }
这里的getHandlerInternal(request)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只用实现该方法中的一部分),如下
1. public class BeanNameUrlHandlerMapping extends
2.
3. /**
4. * Checks name and aliases of the given bean for URLs, starting with "/".
5. */
6. @Override
7. protected
8. new
9. if (beanName.startsWith("/")) {
10. urls.add(beanName);
11. }
12. String[] aliases = getApplicationContext().getAliases(beanName);
13. for
14. if (alias.startsWith("/")) {
15. urls.add(alias);
16. }
17. }
18. return
19. }
20.
21. }
这里面注释说,bean的name必须以/开头,它才处理,将信息存储在Map<String, Object> handlerMap中,对于本工程来说就是{'/index':HomeAction对象}。
至此这里完成了第一步,下面开始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具体实现:
1. protected HandlerAdapter getHandlerAdapter(Object handler) throws
2. for (HandlerAdapter ha : this.handlerAdapters) {
3. if
4. "Testing handler adapter [" + ha + "]");
5. }
6. if
7. return
8. }
9. }
10. throw new ServletException("No adapter for handler ["
11. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
12. }
遍历所有的HandlerAdapter,判断他们是否支持这个handler。
我们来看下HttpRequestHandlerAdapter的supports(handler)方法:
1. public class HttpRequestHandlerAdapter implements
2.
3. @Override
4. public boolean
5. //就是判断handler是否实现了HttpRequestHandler接口
6. return (handler instanceof
7. }
8.
9. @Override
10. public
11. throws
12. //若handler实现了HttpRequestHandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑
13. ((HttpRequestHandler) handler).handleRequest(request, response);
14. return null;
15. }
16.
17. @Override
18. public long
19. if (handler instanceof
20. return
21. }
22. return
23. }
24.
25. }
同理SimpleControllerHandlerAdapter也是这样类似的逻辑
1. public class SimpleControllerHandlerAdapter implements
2.
3. @Override
4. public boolean
5. return (handler instanceof
6. }
7.
8. @Override
9. public
10. throws
11.
12. return
13. }
14.
15. @Override
16. public long
17. if (handler instanceof
18. return
19. }
20. return
21. }
22.
23. }
剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。
按照本工程的配置,则SimpleControllerHandlerAdapter是支持HomeAction的,然后就会执行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本质上就会调用HomeAction实现Controller接口的方法。至此就分析完了。
了解过程了之后,然后就是最重要的也是经常配置出问题的地方。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。
DispatcherServlet初始化的时候,会调用一个方法如下:
1. protected void
2. initMultipartResolver(context);
3. initLocaleResolver(context);
4. initThemeResolver(context);
5. //初始化一些HandlerMapping
6. initHandlerMappings(context);
7. //初始化一些HandlerAdapter
8. initHandlerAdapters(context);
9. initHandlerExceptionResolvers(context);
10. initRequestToViewNameTranslator(context);
11. initViewResolvers(context);
12. initFlashMapManager(context);
13. }
这里可以看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法:
1. private void
2. this.handlerMappings = null;
3.
4. if (this.detectAllHandlerMappings) {
5. // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
6. Map<String, HandlerMapping> matchingBeans =
7. class, true, false);
8. if
9. this.handlerMappings = new
10. // We keep HandlerMappings in sorted order.
11. this.handlerMappings);
12. }
13. }
14. else
15. try
16. class);
17. this.handlerMappings = Collections.singletonList(hm);
18. }
19. catch
20. // Ignore, we'll add a default HandlerMapping later.
21. }
22. }
23.
24. // Ensure we have at least one HandlerMapping, by registering
25. // a default HandlerMapping if no other mappings are found.
26. if (this.handlerMappings == null) {
27. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
28. if
29. "No HandlerMappings found in servlet '" + getServletName() + "': using default");
30. }
31. }
32. }
detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。
1. /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
2. private boolean detectAllHandlerMappings = true;
本工程由于没有配置HandlerMapping,所以它会去加载默认的,下面看看默认的配置是什么
1. protected
2. String key = strategyInterface.getName();
3. //defaultStrategies存储了默认的配置
4. String value = defaultStrategies.getProperty(key);
5. if (value != null) {
6. String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
7. new
8. for
9. try
10. class.getClassLoader());
11. Object strategy = createDefaultStrategy(context, clazz);
12. strategies.add((T) strategy);
13. }
14. catch
15. throw new
16. "Could not find DispatcherServlet's default strategy class ["
17. "] for interface [" + key + "]", ex);
18. }
19. catch
20. throw new
21. "Error loading DispatcherServlet's default strategy class ["
22. "] for interface [" + key + "]: problem with class file or dependent class", err);
23. }
24. }
25. return
26. }
27. else
28. return new
29. }
30. }
继续看看defaultStrategies是如何初始化的:
1. private static final
2.
3. static
4. // Load default strategy implementations from properties file.
5. // This is currently strictly internal and not meant to be customized
6. // by application developers.
7. try
8. //这里的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties
9. new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
10. defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
11. }
12. catch
13. throw new IllegalStateException("Could not load 'DispatcherServlet.properties': "
14. }
15. }
这里使用静态代码块来加载配置文件DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目录下面的,如下图所示:
该默认的配置文件的内容如下:
1. # Default implementation classes for
2. # Used as fallback when no matching beans are found in the DispatcherServlet context.
3. # Not meant to be customized by application developers.
4.
5. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
6.
7. org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
8.
9. #这里就是默认的HandlerMapping的配置
10. org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
11. org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
12. #这里就是默认的HandlerAdapter的配置
13. org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
14. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
15. org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
16.
17. org.springframework.web.servlet.HandlerExceptinotallow=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
18. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
19. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
20.
21. org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
22.
23. org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
24.
25. 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方法。