SpringMVC源码分析-DispatcherServlet

DispatcherServlet 处理流程

在整个 Spring MVC 框架中,DispatcherServlet 处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应工作。在看 DispatcherServlet 类之前,我们先来看一下请求处理的大致流程:

  1. Tomcat 启动,对 DispatcherServlet 进行实例化,然后调用它的 init() 方法进行初始化,在这个初始化过程中完成了:
  • 对 web.xml 中初始化参数的加载;
  • 建立 WebApplicationContext (SpringMVC的IOC容器);
  • 进行组件的初始化;
  1. 客户端发出请求,由 Tomcat 接收到这个请求,如果匹配 DispatcherServlet 在 web.xml 中配置的映射路径,Tomcat 就将请求转交给 DispatcherServlet 处理;
  2. DispatcherServlet 从容器中取出所有 HandlerMapping 实例(每个实例对应一个 HandlerMapping 接口的实现类)并遍历,每个 HandlerMapping 会根据请求信息,通过自己实现类中的方式去找到处理该请求的 Handler (执行程序,如Controller中的方法),并且将这个 Handler 与一堆 HandlerInterceptor (拦截器) 封装成一个 HandlerExecutionChain 对象,一旦有一个 HandlerMapping 可以找到 Handler 则退出循环;
  3. DispatcherServlet 取出 HandlerAdapter 组件,根据已经找到的 Handler,再从所有 HandlerAdapter 中找到可以处理该 Handler 的 HandlerAdapter 对象;
  4. 执行 HandlerExecutionChain 中所有拦截器的 preHandler() 方法,然后再利用 HandlerAdapter 执行 Handler ,执行完成得到 ModelAndView,再依次调用拦截器的 postHandler() 方法;
  5. 利用 ViewResolver 将 ModelAndView 或是 Exception(可解析成 ModelAndView)解析成 View,然后 View 会调用 render() 方法再根据 ModelAndView 中的数据渲染出页面;
  6. 最后再依次调用拦截器的 afterCompletion() 方法,这一次请求就结束了。

SpringMVC源码分析-DispatcherServlet

DispatcherServlet 继承自 HttpServlet,它遵循 Servlet 里的“init-service-destroy”三个阶段,首先我们先来看一下它的 init() 阶段。

public class DispatcherServlet extends FrameworkServlet {
...
}

SpringMVC源码分析-DispatcherServlet

初始化

HttpServletBean 的 init() 方法

DispatcherServlet 的 init() 方法在其父类 HttpServletBean 中实现的,它覆盖了 GenericServlet 的 init() 方法,主要作用是加载 web.xml 中 DispatcherServlet 的 配置,并调用子类的初始化。下面是 init() 方法的具体代码:

@Override
public final void init() throws ServletException {
	// ServletConfigPropertyValues 是静态内部类,使用 ServletConfig 获取 web.xml 中配置的参数
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			// 使用 BeanWrapper 来构造 DispatcherServlet
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}catch (BeansException ex) {
			throw ex;
		}
	}
	// 让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式
	initServletBean();
}

web.xml 中 DispatcherServlet 的 配置

  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
                classpath:servlet-context.xml
            </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

FrameworkServlet 的 initServletBean() 方法

在 HttpServletBean 的 init() 方法中调用了 initServletBean() 这个方法,它是在 FrameworkServlet 类中实现的,主要作用是建立 WebApplicationContext 容器(有时也称上下文),并加载 SpringMVC 配置文件中定义的 Bean 到改容器中,最后将该容器添加到 ServletContext 中。下面是 initServletBean() 方法的具体代码:

@Override
protected final void initServletBean() throws ServletException {
    try {
        // 初始化 WebApplicationContext (即SpringMVC的IOC容器)
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    } catch (ServletException ex) {
    } catch (RuntimeException ex) {
    }
}

WebApplicationContext 继承于 ApplicationContext 接口,从容器中可以获取当前应用程序环境信息,它也是 SpringMVC 的 IOC 容器。下面是 initWebApplicationContext() 方法的具体代码:

protected WebApplicationContext initWebApplicationContext() {
    // 获取 ContextLoaderListener 初始化并注册在 ServletContext 中的根容器,即 Spring 的容器
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    if (this.webApplicationContext != null) {
        // 因为 WebApplicationContext 不为空,说明该类在构造时已经将其注入
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    // 将 Spring 的容器设为 SpringMVC 容器的父容器
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
      // 如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。
      wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 如果 WebApplicationContext 仍为空,则以 Spring 的容器为父上下文建立一个新的。
        wac = createWebApplicationContext(rootContext);
    }
    if (!this.refreshEventReceived) {
        // 模版方法,由 DispatcherServlet 实现
        onRefresh(wac);
    }
    if (this.publishContext) {
        // 发布这个 WebApplicationContext 容器到 ServletContext 中
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}

下面是查找 WebApplicationContext 的 findWebApplicationContext() 方法代码:

protected WebApplicationContext findWebApplicationContext() {
    String attrName = getContextAttribute();
    if (attrName == null) {
        return null;
    }
    // 从 ServletContext 中查找已经发布的 WebApplicationContext 容器
    WebApplicationContext wac =
    WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
    if (wac == null) {
        throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
    }
    return wac;
}

DispatcherServlet 的 onRefresh() 方法

建立好 WebApplicationContext(上下文) 后,通过 onRefresh(ApplicationContext context) 方法回调,进入 DispatcherServlet 类中。onRefresh() 方法,提供 SpringMVC 的初始化,具体代码如下:

@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
	//初始化文件上传处理类
	initMultipartResolver(context);
	//初始化本地化Resolver
	initLocaleResolver(context);
	//初始化主题Resolver
	initThemeResolver(context);
	//初始化一些个与处理的HandlerMappings
	initHandlerMappings(context);
	initHandlerAdapters(context);
	//初始化异常处理的handler
	initHandlerExceptionResolvers(context);
	//初始化请求路径转换为ViewName 的Translator
	initRequestToViewNameTranslator(context);
	//初始化ViewResolvers 这个就是针对视图处理的Resolvers 比如jsp处理Resolvers 或者freemarker处理Resolvers
	initViewResolvers(context);
	//初始化 主要管理flashmap,比如RedirectAttributes 的属性会放到这个里面,默认使用的是SessionFlashMapManager
	initFlashMapManager(context);
}

在 initStrategies() 方法中进行了各个组件的初始化,先来看一下这些组件的初始化方法,稍后再来详细分析这些组件。

initHandlerMappings 方法

initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list 中。这个方法并不是对 HandlerMapping 实例的创建,HandlerMapping 实例是在上面 WebApplicationContext 容器初始化,即 SpringMVC 容器初始化的时候创建的。

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    if (this.detectAllHandlerMappings) {
        // 从 SpringMVC 的 IOC 容器及 Spring 的 IOC 容器中查找 HandlerMapping 实例
        Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
            // 按一定顺序放置 HandlerMapping 对象
            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.
        }
    }
    // 如果没有 HandlerMapping,则加载默认的
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    }
}

initHandlerAdapters 方法

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;
    if (this.detectAllHandlerAdapters) {
        // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerAdapter> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
            // We keep HandlerAdapters in sorted order.
            OrderComparator.sort(this.handlerAdapters);
        }
    } else {
        try {
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        } catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }
    // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
    }
}

处理请求

HttpServlet 提供了 doGet()、doPost() 等方法,DispatcherServlet 中这些方法是在其父类 FrameworkServlet 中实现的,代码如下:

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}

这些方法又都调用了 processRequest() 方法,我们来看一下代码:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    // 返回与当前线程相关联的 LocaleContext
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    // 根据请求构建 LocaleContext,公开请求的语言环境为当前语言环境
    LocaleContext localeContext = buildLocaleContext(request);
    
    // 返回当前绑定到线程的 RequestAttributes
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    // 根据请求构建ServletRequestAttributes
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    
    // 获取当前请求的 WebAsyncManager,如果没有找到则创建
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    // 使 LocaleContext 和 requestAttributes 关联
    initContextHolders(request, localeContext, requestAttributes);

    try {
        // 由 DispatcherServlet 实现
        doService(request, response);
    } catch (ServletException ex) {
    } catch (IOException ex) {
    } catch (Throwable ex) {
    } finally {
        // 重置 LocaleContext 和 requestAttributes,解除关联
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }// 发布 ServletRequestHandlerEvent 事件
        publishRequestHandledEvent(request, startTime, failureCause);
    }
}

DispatcherServlet 的 doService() 方法主要是设置一些 request 属性,并调用 doDispatch() 方法进行请求分发处理,doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView ,ModelAndView 是连接“业务逻辑层”与“视图展示层”的桥梁,接下来就要通过 ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染。doDispatch() 方法如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    // 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
            // 检查是否有 Multipart,有则将请求转换为 Multipart 请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            // 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            // 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 处理 last-modified 请求头 
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            // 遍历拦截器,执行它们的 preHandle() 方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            try {
                // 执行实际的处理程序
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            } finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }
            applyDefaultViewName(request, mv);
            // 遍历拦截器,执行它们的 postHandle() 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        }
        // 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } catch (Exception ex) {
    } catch (Error err) {
    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // 遍历拦截器,执行它们的 afterCompletion() 方法  
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            return;
        }
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
        }
    }
}

HandlerMapping

作用是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecutionChain 对象中。在 HandlerMapping 接口的内部只有一个方法,如下:

public interface HandlerMapping {

	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

HandlerMapping 是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler,而 HandlerMapping 具体有哪些实现类下面就会详细分析。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 这些 HandlerMapping 在容器初始化时创建,在 initHandlerMappings 时放入集合中
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

另外上面说到的 Handler 有可能是一个 HandlerMethod(封装了 Controller 中的方法)对象,也有可能是一个 Controller 对象、 HttpRequestHandler 对象或 Servlet 对象,而这个 Handler 具体是什么对象,也是与所使用的 HandlerMapping 实现类有关。如下图所示,可以看到 HandlerMapping 实现类有两个分支,分别继承自 AbstractHandlerMethodMapping(得到 HandlerMethod)和 AbstractUrlHandlerMapping(得到 HttpRequestHandler、Controller 或 Servlet),它们又统一继承于 AbstractHandlerMapping。
SpringMVC源码分析-DispatcherServlet


先来看一下 AbstractHandlerMapping,它实现了 HandlerMapping 接口中的 getHandler() 方法,源码如下所示
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 根据请求获取执行程序,具体的获取方式由子类决定,getHandlerInternal() 是抽象方法
    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);
    }
    // 将 Handler 与一堆拦截器包装到 HandlerExecutionChain 对象中
    return getHandlerExecutionChain(handler, request);
}

可以看到在这个方法中又调用了 getHandlerInternal() 方法获取到了 Handler 对象,而 Handler 对象具体内容是由它的子类去定义的。下面就来一看下 AbstractHandlerMapping 的两个分支子类

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping 这个分支获取的 Handler 的类型实际就是一个 Controller 类,所以一个 Controller 只能对应一个请求(或者像 Struts2 那样定位到方法,使同一个业务的方法放在同一个类里),源码如下所示:

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    // 根据当前请求获取“查找路径”
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 根据路径获取 Handler(即Controller),先尝试直接匹配,再尝试模式匹配
    Object handler = lookupHandler(lookupPath, request);
    if (handler == null) {
       // We need to care for the default handler directly, since we need to
        // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
        Object rawHandler = null;
        if ("/".equals(lookupPath)) {
            rawHandler = getRootHandler();
        }
        if (rawHandler == null) {
            rawHandler = getDefaultHandler();
        }
        if (rawHandler != null) {
            // Bean name or resolved handler?
            if (rawHandler instanceof String) {
                String handlerName = (String) rawHandler;
                rawHandler = getApplicationContext().getBean(handlerName);
            }
            validateHandler(rawHandler, request);
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
        }
    }
    return handler;
}

SpringMVC源码分析-DispatcherServlet

ControllerClassNameHandlerMapping

根据控制类的类名找到对应的Controller,访问时类名首字母小写

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.ControllerClassNameHandlerMapping" />
<!-- 注册 Handler -->
<bean class="com.controller.TestController" />
 <a href="testController.do?mapType=3">根据Controller类名找到对应的Controller</a><br/>

ControllerBeanNameHandlerMapping

根据 Bean 名访问 Controller,与 BeanNameUrlHandlerMapping 类似,但是bean名称不用遵循URL公约。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.ControllerBeanNameHandlerMapping" />
<!-- 注册 Handler -->
<bean id="test" class="com.controller.TestController" />

BeanNameUrlHandlerMapping

利用 BeanName 来作为 URL 使用。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 注册 Handler -->
<bean id="/test.do" class="com.controller.TestController" />
<a href="test.do?mapType=1">根据beanName找到对应的Controller</a><br/>

SimpleUrlHandlerMapping

可以将 URL 与处理器的定义分离,还可以对 URL 进行统一的映射管理。

<!-- 注册 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/test.do">testController</prop>
            <prop key="/hello.do">testController</prop>
        </props>
    </property>
</bean>
<!-- 注册 Handler -->
<bean id="testController" class="com.controller.TestController" />
<a href="test.do?mapType=2">根据URL找到对应的Controller</a><br/>  

Controller 类

使用 AbstractUrlHandlerMapping 的实现类时,需要让控制层的类实现 Controller 接口(一般继承 AbstractController 即可),另外还有一些已经实现了的 Controller 类,如下图所示。但是不论是自己实现 Controller 接口还是使用系统已经实现的类,都只能处理一个请求(除了 MultiActionController 可以通过参数的方式让一个类可以处理多个请求)。
SpringMVC源码分析-DispatcherServlet

另外下面所有的 Controller 均采用 SimpleUrlHandlerMapping 方式的。

UrlFilenameViewController

用于跳转界面,控制器根据请求的URL直接解析出视图名,省去了自己实现 Ccntroller 跳转页面。

<bean id="indexController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />

ParameterizableViewController

同样用于界面跳转,控制器根据配置的参数来跳转界面,使用方式如下

<bean id="indexController" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
     <property name="viewName" value="/index.jsp" />
</bean>

ServletForwardingController

将请求转发到 Servlet,使用方式如下

<bean id="indexController" class="org.springframework.web.servlet.mvc.ServletForwardingController">    
    <property name="servletName" value="indexServlet" />    
</bean> 

另外还要在 web.xml 中配置要转发到的 Servlet

<servlet>    
    <servlet-name>indexServlet</servlet-name>    
    <servlet-class>com.servlet.ServletForwarding</servlet-class>    
</servlet>   

ServletWrappingController

将某个 Servlet 包装为 Controller,所有到 ServletWrappingController 的请求实际上是由它内部所包装的这个 Servlet 实例来处理的,这样可以将这个 Servlet 隐藏起来。

MultiActionController

一个 Controller 可以写多个方法,分别对应不同的请求,使同一业务的方法可以放在一起了。在使用时让自己的 Controller 类继承 MultiActionController 类,使用方式如下

public class IndexController extends MultiActionController {  
    public ModelAndView add(HttpServletRequest request,HttpServletResponse response) {  
        ModelAndView mv = new ModelAndView();   
        mv.addObject("message","add");   
        mv.setViewName("add");   
        return mv;   
    }  
    public ModelAndView delete(HttpServletRequest request,HttpServletResponse response) {  
        ModelAndView mv = new ModelAndView();   
        mv.addObject("message","delete");   
        mv.setViewName("delete");   
        return mv;   
    }  
}

配置自己的 Controller 时要配置一个方法名解析器(默认是 InternalPathMethodNameResolver )

<bean id="indexController" class="com.controller.IndexController">  
      <property name="methodNameResolver">  
        <!-- InternalPathMethodNameResolver 根据请求路径解析执行方法名
             ParameterMethodNameResolver 根据参数解析执行方法名
             PropertiesMethodNameResolver 根据 key/value 列表解析执行方法名 -->
        <bean class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">  
           <!-- 指定参数名为action -->  
           <property name="paramName" value="action" />  
        </bean>  
      </property>  
</bean>

当我们访问 http://localhost:8080/***/indexAction.do?action=add 时,进入 add() 方法;

当我们访问 http://localhost:8080/***/indexAction.do?action=delete 时,进入 delete() 方法。

AbstractHandlerMethodMapping

AbstractHandlerMethodMapping 这个分支获取的 Handler 的类型是 HandlerMethod,即这个 Handler 是一个方法,它保存了方法的信息(如Method),这样一个 Controller 就可以处理多个请求了,源码如下所示:

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 根据当前请求获取“查找路径”
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 获取当前请求最佳匹配的处理方法(即Controller类的方法中)
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}

上述代码中 lookupHandlerMethod() 方法主要工作是在 Map<T, HandlerMethod> handlerMethods 中找到 HandlerMethod,这里的 T 是 HandlerMappingInfo,它封装了 @RequestMapping 注解中的信息。那 HandlerMethod 是怎么创建的(即怎么把 Controller 的方法变成了它),继续看一下源码找到 initHandlerMethods() 方法,这个方法是在这个类创建后调用的,如下所示是它的源码

protected void initHandlerMethods() {
    // 从容器中获取所有 Bean 的名称,detectHandlerMethodsInAncestorContexts 默认false,不从父容器中查找
    //即默认只查找 SpringMVC 的 IOC 容器,不查找它的父容器 Spring 的 IOC 容器
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
        getApplicationContext().getBeanNamesForType(Object.class));
    for (String beanName : beanNames) {
        // 这里的 isHandler()方法由子类实现,判断是否拥有 @Controller 注解或 @RequestMapping 注解
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) && isHandler(getApplicationContext().getType(beanName))){
            // 利用反射得到 Bean 中的 Method 并包装成 HandlerMethod,然后放入 Map 中
            detectHandlerMethods(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

看完上述代码后,可以知道是在 detectHandlerMethods() 方法中将 Bean 的方法转换为 HandlerMethod 对象,具体实现如下:

protected void detectHandlerMethods(final Object handler) {
    // 获取这个 Bean 的 Class 对象
    Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
    // 避免重复调用 getMappingForMethod(),getMappingForMethod() 将重新构建 RequestMappingInfo 实例
    final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
    // 获取被代理前的原始类型
    final Class<?> userType = ClassUtils.getUserClass(handlerType);
    // 获取 Method  
    Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
        @Override
        public boolean matches(Method method) {
            // 根据 Method 和它的 @RequestMapping 注解,创建 RequestMappingInfo 对象。
            // 这里的 T 就是 RequestMappingInfo,它封装了 @RequestMapping 信息
            T mapping = getMappingForMethod(method, userType);
            if (mapping != null) {
                mappings.put(method, mapping);
                return true;
            } else {
                return false;
            }
        }
    });
    for (Method method : methods) {
        // 注册 Method 和它的映射,RequestMappingInfo 储存着映射信息
        registerHandlerMethod(handler, method, mappings.get(method));
    }
}

最后在 registerHandlerMethod() 方法中,将 RequestMappingInfo 作为 key,把 Method 包装成 HandlerMethod 作为 value 添加到了 Map<T, HandlerMethod> handlerMethods 中。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
    HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
    if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
        throw new IllegalStateException("");
    }
    this.handlerMethods.put(mapping, newHandlerMethod);
    Set<String> patterns = getMappingPathPatterns(mapping);
    for (String pattern : patterns) {
        if (!getPathMatcher().isPattern(pattern)) {
            this.urlMap.add(pattern, mapping);
        }
    }
}

RequestMappingHandlerMapping

HandlerAdapter

根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象。HandlerAdapter 接口中的方法如下:

public interface HandlerAdapter {

	// 当前 HandlerAdapter 是否支持这个 Handler
	boolean supports(Object handler);

	// 利用 Handler 处理请求
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	long getLastModified(HttpServletRequest request, Object handler);

}

SpringMVC源码分析-DispatcherServlet

RequestMappingHandlerAdapter

从上面的文章中可以知道,利用 RequestMappingHandlerMapping 获取的 Handler 是 HadnlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HadnlerMethod 对象。

RequestMappingHandlerAdapter 的 handle() 方法是在它的父类 AbstractHandlerMethodAdapter 类中实现的,源码如下所示:

@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal() 方法是由 RequestMappingHandlerAdapter 自己来实现的,源码如下所示:

@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 是否通过 @SessionAttributes 注释声明了 session 属性。
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    } else {
        checkAndPrepare(request, response, true);
    }
    // 是否需要在 synchronize 块中执行
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                // 执行 HandlerMethod
                return invokeHandleMethod(request, response, handlerMethod);
            }
        }
    }
    // 执行 HandlerMethod,得到 ModelAndView
    return invokeHandleMethod(request, response, handlerMethod);
}

继续再来看一下如何得到 ModelAndView,invokeHandlerMethod() 方法如下:

private ModelAndView invokeHandleMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    // 数据绑定
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    // 绑定参数,执行方法
    ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
    // 创建模型和视图容器
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    // 设置FlasgMap中的值
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    // 初始化模型
    modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

    AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    asyncWebRequest.setTimeout(this.asyncRequestTimeout);

    final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.setTaskExecutor(this.taskExecutor);
    asyncManager.setAsyncWebRequest(asyncWebRequest);
    asyncManager.registerCallableInterceptors(this.callableInterceptors);
    asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    if (asyncManager.hasConcurrentResult()) {
        Object result = asyncManager.getConcurrentResult();
        mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
        asyncManager.clearConcurrentResult();
        requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
    }
    requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
    if (asyncManager.isConcurrentHandlingStarted()) {
        return null;
    }
    return getModelAndView(mavContainer, modelFactory, webRequest);
}

HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 可以执行 HttpRequestHandler 类型的 Handler,源码如下

public class HttpRequestHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((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

SimpleControllerHandlerAdapter 可以执行 Controller 类型的 Handler,源码如下:

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;
	}

}

SimpleServletHandlerAdapter

SimpleServletHandlerAdapter 可以执行 Servlet 类型的 Handler,源码如下

public class SimpleServletHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Servlet);
	}

	@Override
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((Servlet) handler).service(request, response);
		return null;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		return -1;
	}

}

HandlerExceptionResolver

负责处理异常的类,负责根据异常来设置 ModelAndView,然后交由 render 渲染界面。HandlerExecptionResolver 接口中只有一个方法,如下:

public interface HandlerExceptionResolver {

	ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
	
}

SpringMVC源码分析-DispatcherServlet

ViewResolver

根据视图的名称将其解析为 View 类型的视图,如通过 ModelAndView 中的视图名称将其解析成 View,View 是用来渲染页面的,也就是将 Model 填入模板中,生成 html 或其他格式的文件。

可以设置多个解析策略,如可以根据 JSP 来解析,或者按照 Velocity 模版解析,如果设置了多个解析策略则可以通过 order 属性来设定其优先级,数值越小优先级越高,前面的视图解析器解析后就不会让后面的继续解析。默认的解析策略是 InternalResourceViewResolver,按照 JSP 页面来解析。ViewResolver 接口中的方法如下:

public interface ViewResolver {

	View resolveViewName(String viewName, Locale locale) throws Exception;

}

SpringMVC源码分析-DispatcherServlet

AbstractCachingViewResolver

AbstractCachingViewResolver 是带有缓存的 ViewResolver,它每次解析时先从缓存里查找,如果找到视图就返回,没有就创建新的视图,且创建新视图的方法由其子类实现,具体代码如下所示:

@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
    // 是否启用缓存,可通过setCache()方法或setCacheLimit()方法开启缓存,是一个ConcurrentHashMap,默认缓存大小1024
    if (!isCache()) {
        return createView(viewName, locale);
    } else {
        // 得到 view 在缓存中的 key 值
        Object cacheKey = getCacheKey(viewName, locale);
        View view = this.viewAccessCache.get(cacheKey);
        // 如果没有找到 view 则创建,采用双重校验的方式进行安全创建
        if (view == null) {
            synchronized (this.viewCreationCache) {
                view = this.viewCreationCache.get(cacheKey);
                if (view == null) {
                    // 具体的创建方式由子类实现
                    view = createView(viewName, locale);
                    if (view == null && this.cacheUnresolved) {
                        view = UNRESOLVED_VIEW;
                    }
                    if (view != null) {
                        this.viewAccessCache.put(cacheKey, view);
                        this.viewCreationCache.put(cacheKey, view);
                    }
                }
            }
        }
        return (view != UNRESOLVED_VIEW ? view : null);
    }
}

ResourceBundleViewResolver

ResourceBundleViewResolver 根据 views.properties 文件来解析视图,这个文件位于 classpath 路径下,使用方式如下:

<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">  
    <!-- 设定属性文件名为views -->  
    <property name="basename" value="views"></property>  
</bean>  

XmlViewResolver

XmlViewResolver 根据 xml 文件来解析视图,使用方式如下:

<bean class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="location">
        <value>/WEB-INF/spring-views.xml</value>
    </property>
</bean>

UrlBasedViewResolver

UrlBasedViewResolver 提供了拼接 URL 的方式来解析视图,通过 prefix 属性拼接一个前缀,通过 suffix 属性拼接一个后缀,就得到了视图的 URL。还可以加入 redirect: 与 forword: 前缀,使用 redirect: 前缀会调用 HttpServletResponse对象的 sendRedirect() 方法进行重定向,使用 forword: 前缀会利用 RequestDispatcher的forword 方式跳转到指定的地址。另外,使用时还要指定 viewClass 属性,表示要解析成哪种 View,的使用方式如下:

<bean>
   class="org.springframework.web.servlet.view.UrlBasedViewResolver">  
   <property name="prefix" value="/WEB-INF/" />  
   <property name="suffix" value=".jsp" />  
   <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>  
</bean>  

InternalResourceViewResolver

InternalResourceViewResolver 是 UrlBasedViewResolver 的子类,将 InternalResourceView 作为默认的 View 类,但如果当前classpath 中有 jstl 的 jar 包时则使用 JstlView 作为 view 来渲染。

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
       <property name="prefix" value="/WEB-INF/jsp/" />
       <property name="suffix" value=".jsp" />
</bean>

BeanNameViewResolver

BeanNameViewResolver 是通过视图名称去容器中获取对应的 view 对像,所以在使用前需要将 view 对象注册到容器中。它没有缓存,实现方式如下:

@Override
public View resolveViewName(String viewName, Locale locale) throws BeansException {
    ApplicationContext context = getApplicationContext();
    if (!context.containsBean(viewName)) {
        // Allow for ViewResolver chaining...
        return null;
    }
    if (!context.isTypeMatch(viewName, View.class)) {
        // Since we're looking into the general ApplicationContext here,
        // let's accept this as a non-match and allow for chaining as well...
        return null;
    }
    return context.getBean(viewName, View.class);
}

MultipartResolver

MultipartResolver 用于处理文件上传,当收到请求时 DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中是否包含文件。如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest (继承了 HttpServletRequest) 对象中,最后传递给 Controller,在 MultipartResolver 接口中有如下方法:

public interface MultipartResolver {
	// 是否是 multipart
	boolean isMultipart(HttpServletRequest request);
	// 解析请求
	MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;

	void cleanupMultipart(MultipartHttpServletRequest request);

}

MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空。在 MultipartFile 接口中有如下方法:

  • String getName(); // 获取参数的名称
  • String getOriginalFilename(); // 获取文件的原名称
  • String getContentType(); // 文件内容的类型
  • boolean isEmpty(); // 文件是否为空
  • long getSize(); // 文件大小
  • byte[] getBytes(); // 将文件内容以字节数组的形式返回
  • InputStream getInputStream(); // 将文件内容以输入流的形式返回
  • void transferTo(File dest); // 将文件内容传输到指定文件中

MultipartResolver 是一个接口,它的实现类如下图所示,分为 CommonsMultipartResolver 类和 StandardServletMultipartResolver 类。
SpringMVC源码分析-DispatcherServlet

其中 CommonsMultipartResolver 使用 commons Fileupload 来处理 multipart 请求,所以在使用时,必须要引入相应的 jar 包;而 StandardServletMultipartResolver 是基于 Servlet 3.0来处理 multipart 请求的,所以不需要引用其他 jar 包,但是必须使用支持 Servlet 3.0的容器才可以,以tomcat为例,从 Tomcat 7.0.x的版本开始就支持 Servlet 3.0了。

CommonsMultipartResolver

CommonsMultipartResolver 实现了 MultipartResolver 接口,resolveMultipart() 方法如下所示,其中 resolveLazily 是判断是否要延迟解析文件(通过XML可以设置)。当 resolveLazily 为 flase 时,会立即调用 parseRequest() 方法对请求数据进行解析,然后将解析结果封装到 DefaultMultipartHttpServletRequest 中;而当 resolveLazily 为 true 时,会在 DefaultMultipartHttpServletRequest 的 initializeMultipart() 方法调用 parseRequest() 方法对请求数据进行解析,而 initializeMultipart() 方法又是被 getMultipartFiles() 方法调用,即当需要获取文件信息时才会去解析请求数据,这种方式用了懒加载的思想。

@Override
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
    Assert.notNull(request, "Request must not be null");
    if (this.resolveLazily) {
        //懒加载,当调用DefaultMultipartHttpServletRequest的getMultipartFiles()方法时才解析请求数据
        return new DefaultMultipartHttpServletRequest(request) {
            @Override //当getMultipartFiles()方法被调用时,如果还未解析请求数据,则调用initializeMultipart()方法进行解析 protected void initializeMultipart() {
                MultipartParsingResult parsingResult = parseRequest(request);
                setMultipartFiles(parsingResult.getMultipartFiles());
                setMultipartParameters(parsingResult.getMultipartParameters());
                setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
            }
        };
    } else {
        //立即解析请求数据,并将解析结果封装到DefaultMultipartHttpServletRequest对象中
        MultipartParsingResult parsingResult = parseRequest(request);
        return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(), 
              parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
    }
}

在上面的代码中可以看到,对请求数据的解析工作是在 parseRequest() 方法中进行的,继续看一下 parseRequest() 方法源码

protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
    // 获取请求的编码类型
    String encoding = determineEncoding(request);
    FileUpload fileUpload = prepareFileUpload(encoding);
    try {
        List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
        return parseFileItems(fileItems, encoding);
    } catch (...) {}
}

在 parseRequest() 方法中,首先调用了 prepareFileUpload() 方法来根据编码类型确定一个 FileUpload 实例,然后利用这个 FileUpload 实例解析请求数据后得到文件信息,最后将文件信息解析成 CommonsMultipartFile (实现了 MultipartFile 接口) 并包装在 MultipartParsingResult 对象中。

StandardServletMultipartResolver

StandardServletMultipartResolver 实现了 MultipartResolver 接口,resolveMultipart() 方法如下所示,其中 resolveLazily 是判断是否要延迟解析文件(通过XML可以设置)。

private void parseRequest(HttpServletRequest request) {
    try {
        Collection<Part> parts = request.getParts();
        this.multipartParameterNames = new LinkedHashSet<String>(parts.size());
        MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<String, MultipartFile>(parts.size());
        for (Part part : parts) {
            String disposition = part.getHeader(CONTENT_DISPOSITION);
            String filename = extractFilename(disposition);
            if (filename == null) {
                filename = extractFilenameWithCharset(disposition);
            }
            if (filename != null) {
                files.add(part.getName(), new StandardMultipartFile(part, filename));
            } else {
                this.multipartParameterNames.add(part.getName());
            }
        }
        setMultipartFiles(files);
    } catch (Throwable ex) {}
}

parseRequest() 方法利用了 servlet3.0 的 request.getParts() 方法获取上传文件,并将其封装到 MultipartFile 对象中。