spring mvc源码解析

2018-02-06 20:41:29

源码分析基于spring 4.3.x
本文主要记录看spring mvc源码时的一些关键点。不当之处,还望指出。

HttpServlet接口结构

HttpServlet.png

HttpServletBean

HttpServletBean是接口结构第一个Spring的类,它从web.xml或WebApplicationInitializer接收servlet的init-param值,注入到bean的属性。

FrameworkServlet

FrameworkServlet集成了Servlet功能与Spring web application context上下文,实现了ApplicationContextAware接口。 但它也能够自行创建web application context。

HttpServletBean父类将init-params注入为bean属性。

HttpServlet中doGet/doPost/doPut/doDelete等方法,统一调用processRequest方法处理(该方法最终会调用DispatcherServlet.doService)

DispatcherServlet

DispatcherServlet是springmvc的核心类,实现了doService方法,
负责请求准备,Flash映射,请求分派(doDispatch),异常处理等工作。

doDispatch抽取核心代码如下

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {
        ModelAndView mv = null;
        try {
            // 查找handler
            mappedHandler = getHandler(processedRequest);

            // 查找HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 调用HandlerInterceptor.preHandle
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 调用handler处理
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            applyDefaultViewName(processedRequest, mv);

            // 调用HandlerInterceptor.preHandlepostHandle
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            ...
        }

        // 结果处理
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    finally {
        // 资源清理
        ...
    }
}

HandlerMapping

HandlerMapping.png

HandlerMapping负责定义requests和handler的映射关系。

AbstractHandlerMapping.getHandler获取HandlerExecutionChain,HandlerExecutionChain是对handler的一个扩展,包含了handler(一般是HandlerMethod)和interceptors
AbstractHandlerMapping.getHandlerInternal获取对应的处理方法HandlerMethod,通过lookupHandlerMethod方法查找。

AbstractHandlerMethodMapping 实现了InitializingBean, afterPropertiesSet方法查找并解析所有RequestMapping注解的方法,并将结果存储在mappingRegistry属性中。

HandlerAdapter

HandlerAdapter.png

HandlerAdapter负责使用handler处理requests,核心方法ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
spring中提供了多种HandlerAdapter, SimpleServletHandlerAdapter/SimpleControllerHandlerAdapter/RequestMappingHandlerAdapter(springmvc使用)。

RequestMappingHandlerAdapter.invokeHandlerMethod方法具体实现了方法调用

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        // 获取方法
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

        invocableMethod.invokeAndHandle(webRequest, mavContainer);

        return getModelAndView(mavContainer, modelFactory, webRequest);

}

ServletInvocableHandlerMethod.invokeAndHandle负责参数解析,方法调用,结果处理。

参数解析

接口结构

HandlerMethod.png

HandlerMethod是对RequestMapping映射方法的抽象。

InvocableHandlerMethod.getMethodArgumentValues

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {
    // 获取方法参数
    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = resolveProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        // argumentResolvers是否支持
        if (this.argumentResolvers.supportsParameter(parameter)) {
                //     argumentResolvers解析
                args[i] = this.argumentResolvers.resolveArgument(
                        parameter, mavContainer, request, this.dataBinderFactory);
                continue;
            }

        }
        ...
    }
    return args;
}

参数解析通过HandlerMethodArgumentResolver完成。
HandlerMethodArgumentResolver.png

RequestParamMethodArgumentResolver负责处理@RequestParam注解的参数
PathVariableMethodArgumentResolver负责处理@PathVariable注解的参数
RequestResponseBodyMethodProcessor负责处理 @RequestBody 注解,
从AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters中可以看到,它会根据HttpReqeust的contentType,查找对应的HttpMessageConverter进行处理。
AbstractMessageConverterMethodArgumentResolver.messageConverters属性保存所有的HttpMessageConverter,包括json, xml的转化。

结果处理

方法调用结果有两个地方处理。

  1. ServletInvocableHandlerMethod.handleReturnValue
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
     // 查找HandlerMethodReturnValueHandler
     HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
     // 结果处理
     handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
    
    我们一般使用@ResponseBody注解方法,声明返回json结果。@ResponseBody注解也是由RequestResponseBodyMethodProcessor进行处理的。
    AbstractMessageConverterMethodProcessor.writeWithMessageConverters转化结果并输出到客户端。
  2. DispatcherServlet.processDispatchResult负责渲染ModelAndView

HandlerInterceptor

HandlerInterceptor也是我们常用的功能。HandlerInterceptor类似于Servlet 规范中的过滤器Filter,可以对每个request进行预处理和后处理。通过HandlerInterceptor,我们可以实现身份认证,日志等功能。

前面说过了,HandlerExecutionChain中包含了HandlerInterceptor,DispatcherServlet.doDispatch在handler处理前后会调用mappedHandler.applyPreHandlemappedHandler.applyPostHandle,而mappedHandler.triggerAfterCompletion在ModelAndView渲染后调用。

异常处理

springmvc提供了多种处理异常的方法,如Controller中定义

@Controller
public class SimpleController {

    // @RequestMapping methods omitted ...

    @ExceptionHandler(IOException.class)
    public ResponseEntity<String> handleIOException(IOException ex) {
        // prepare responseEntity
        return responseEntity;
    }

}

DispatcherServlet.processHandlerException负责处理异常

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
        Object handler, Exception ex) throws Exception {

    // Check registered HandlerExceptionResolvers...
    ModelAndView exMv = null;
    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
        exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
        if (exMv != null) {
            break;
        }
    }
    ...
    throw ex;
}

springmvc提供了三种HandlerExceptionResolver实现,ExceptionHandlerExceptionResolver/DefaultHandlerExceptionResolver/ResponseStatusExceptionResolver,
上面@ExceptionHandler注解方法就是由ExceptionHandlerExceptionResolver进行处理。

我们也可以自定义HandlerExceptionResolver的实现,自行进行异常处理。

可参考 sprimgmvc-1.6. Exception Handling

Spring ApplicationContext层次

springmvc中有两处配置会创建Spring ApplicationContext。

ContextLoaderListener

ContextLoaderListener实现了接口,当web容器启动时,会调用他的contextInitialized方法,web容器关闭时会调用contextDestroyed方法。
ContextLoaderListener会创建ApplicationContext。

先看看web.xml配置

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-core-config.xml</param-value>
    </context-param>

ContextLoaderListener.contextInitialized主要调用ContextLoader.initWebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    ...
    if (this.context == null) {
        // 默认创建XmlWebApplicationContext
        this.context = createWebApplicationContext(servletContext);
    }
    if (this.context instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
        if (!cwac.isActive()) {
            ...
            configureAndRefreshWebApplicationContext(cwac, servletContext);
        }
    }
    // 将ApplicationContext放到 ServletContext
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);


    return this.context;
}

注意:servletContext.setAttribute 这里将这个spring ApplicationContext放到了ServletContext中(后面会使用到它)。

configureAndRefreshWebApplicationContext会找到ServletContext中contextConfigLocation参数(这里是spring-core-config.xml)作为ApplicationContext配置路径,并refresh

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

    ...
    wac.setServletContext(sc);
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    wac.refresh();
}

FrameworkServlet

FrameworkServlet.initServletBean方法也会创建一个spring context。
web.xml的配置

    <servlet>
        <servlet-name>hello-dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-mvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

可以看看他的initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (wac == null) {
        // 从ServletContext中查找WebApplicationContext
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 创建WebApplicationContext
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }

    return wac;
}

这里的rootContext,会找到ContextLoaderListener中创建的WebApplicationContext

看看createWebApplicationContext

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    // 默认XmlWebApplicationContext
    Class<?> contextClass = getContextClass();
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    // 设置parent
    wac.setParent(parent);
    // 设置配置路径
    wac.setConfigLocation(getContextConfigLocation());
    // refresh
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

这里同样将contextConfigLocation参数(这里是spring-mvc-config.xml)作为WebApplicationContext的配置路径。

要注意的是,这里将ContextLoaderListener中创建的WebApplicationContext作为parent了。

ApplicationContext查找bean时,会先从parent context中查找。所以,FrameworkServlet Application可以找到ContextLoaderListener Application中定义的bean,但ContextLoaderListener Application找不到FrameworkServlet Application中定义的bean。这点值得注意,以免出现bean注入失败的异常。