[关闭]
@xialu 2016-05-15T12:15:24.000000Z 字数 5692 阅读 1465

SpringMVC源代码学习(三)DispatcherServlet

SpringMVC源代码学习


以下内容基于书:《看透SpringMVC-源代码分析与实践》基本照搬。。。用于自己查阅备忘。

1. HandlerMapping

HandlerMapping是一个接口,内部只有一个方法和诺干变量,方法如下:
HandlerExecutionChain getHandler(HttpSevletRequest request) throws Exception
接下来看看一个该方法的实现,SimpleControllerHandlerAdapter,代码如下:

  1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  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 String) {
  11. String handlerName = (String) handler;
  12. handler = getApplicationContext().getBean(handlerName);
  13. }
  14. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
  15. if (CorsUtils.isCorsRequest(request)) {
  16. CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
  17. CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
  18. CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
  19. executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
  20. }
  21. return executionChain;
  22. }

另外一个要讨论的就是顺序问题,不同hander负责映射的条件可能有重复的,这时候就需要定义不同的HandlerMapping执行的顺序,这里的顺序可以通过实现Order接口,通过Order属性定义。order越小越先使用。如:

  1. <bean class="com.excelib.TudouHandlerMapping"
  2. p:order="1"/>
  3. <bean class="com.excelib.TudoupianHandlerMapping"
  4. p:order="0"/>

在dispatcherServlet,doDispatch方法中有调用getHandler,代码如下:
其中List< HandlerMapping> handlerMappings是dispatcherServlet的内部变量。
那该方法的内容就是遍历handlerMappings,获得符合条件的HandlerMapping,调用其getHandler方法,返回获得的HandlerExecutionChain

  1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  2. for (HandlerMapping hm : this.handlerMappings) {
  3. if (logger.isTraceEnabled()) {
  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 handler;
  10. }
  11. }
  12. return null;
  13. }

2.HandlerAdapter

在dispatcherServlet通过如下方法获得HandlerAdapter,其中List< HandlerAdapter> handlerAdapters是dispatcherServlet的成员变量,可以看到它的逻辑是遍历所有的Adapter,然后检查哪个可以处理当前的Handler,找到第一个可以处理Handler的Adapter后停止查找,返回。这里的顺序同样是通过Order属性设置的。

  1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  2. for (HandlerAdapter ha : this.handlerAdapters) {
  3. if (logger.isTraceEnabled()) {
  4. logger.trace("Testing handler adapter [" + ha + "]");
  5. }
  6. if (ha.supports(handler)) {
  7. return ha;
  8. }
  9. }
  10. throw new ServletException("No adapter for handler [" + handler +
  11. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  12. }

上篇文章有介绍,HandlerAdapter的角色是使用工具(handler)的人。因为handler是Object类型,需要HandlerAdapter使用它来完成一定格式要求的任务。
它是一个接口,代码如下:

  1. public interface HandlerAdapter {
  2. boolean supports(Object handler);
  3. ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
  4. long getLastModified(HttpServletRequest request, Object handler);

接下来看一个spring自己实现的示例,代码如下:

  1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  2. @Override
  3. public boolean supports(Object handler) {
  4. return (handler instanceof Controller);
  5. }
  6. @Override
  7. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  8. throws Exception {
  9. return ((Controller) handler).handleRequest(request, response);
  10. }
  11. @Override
  12. public long getLastModified(HttpServletRequest request, Object handler) {
  13. if (handler instanceof LastModified) {
  14. return ((LastModified) handler).getLastModified(request);
  15. }
  16. return -1L;
  17. }
  18. }

可以看到这个Adapter比较简单,它要求handler实现了Controller接口,方法的实现是通过处理器的handleRequest方法。

3. HandlerExceptionResolver

HandlerExceptionResolver是SpringMVC中专门负责处理异常的类。它负责:
根据异常设置ModelAndView
之后交给render方法进行渲染。render只负责将Model渲染成页面。具体ModelAndView的来源render并不关心。

  1. public interface HandlerExceptionResolver {
  2. ModelAndView resolveException(
  3. HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
  4. }

它的结构很简单,只有一个方法,只需要从异常解析出ModelAndView就可以了。具体实现可以维护一个异常为Key、View为value的Map,解析时直接从Map里获取View。

4.ViewResolver

ViewResolver用来将String类型的视图名(也叫逻辑视图)和Locale解析为View类型的视图,ViewResolver接口也很简单,代码如下

  1. public interface ViewResolver {
  2. View resolveViewName(String viewName, Locale locale) throws Exception;
  3. }

这里可以看到参数是viewName和locale,不过一般我们只要根据视图名找到视图,然后渲染就可以,如果需要国际化支持也只要将显示的内容或者主题使用国际化支持。
View是用来渲染页面的,也就是将程序返回的参数填入模板中,生成html或其他格式的文件。所以说它会解决两个问题:
1. 填入哪个模板?
2. 如何填入?
它会找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交给不同的视图自己完成。
我们最常用的UrlBasedViewResolver系列的解析器都是针对单一视图类型进行解析的。比如InternalResourceViewResolver只针对jsp类型的视图,FreeMarkerViewResolver只针对FreeMarker,VelocityViewResolver只针对Velocity。而ResourceBundleViewResolver、XmlViewResolver、BeanNameViewResolver等解析器可以同时解析多种类型的视图。
ResourceBundleViewResolver使用properties配置文件来进行配置解析的文件类和url、XmlViewResolver使用xml配置。BeanNameViewResolver是根据ViewName从ApplicationContext容器中查找相应的bean做View的,它比较简单,源码如下:

  1. public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {
  2. private int order = Integer.MAX_VALUE; // default: same as non-Ordered
  3. public void setOrder(int order) {
  4. this.order = order;
  5. }
  6. @Override
  7. public int getOrder() {
  8. return this.order;
  9. }
  10. @Override
  11. public View resolveViewName(String viewName, Locale locale) throws BeansException {
  12. ApplicationContext context = getApplicationContext();
  13. if (!context.containsBean(viewName)) {
  14. if (logger.isDebugEnabled()) {
  15. logger.debug("No matching bean found for view name '" + viewName + "'");
  16. }
  17. // Allow for ViewResolver chaining...
  18. return null;
  19. }
  20. if (!context.isTypeMatch(viewName, View.class)) {
  21. if (logger.isDebugEnabled()) {
  22. logger.debug("Found matching bean for view name '" + viewName +
  23. "' - to be ignored since it does not implement View");
  24. }
  25. // Since we're looking into the general ApplicationContext here,
  26. // let's accept this as a non-match and allow for chaining as well...
  27. return null;
  28. }
  29. return context.getBean(viewName, View.class);
  30. }
  31. }

可以看出原理就是根据viewName从spring容器中查找Bean,if(找不到||不是view类型) return null; else return context.getBean(viewName, View.class);
ViewResolver的使用需要注册到Spring MVC容器中,默认使用的是org.springframework.web.servlet.view.InternalResourceViewResolver。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注