Spring源码解析 -- SpringMvc原理

源码分析基于spring 4.3.x

本文通过阅读Spring MVC的源码,解析Spring MVC实现原理。本文不会深入SpringMvc的细节,关注于分析SpringMvc的各个核心组件以及主要逻辑,以便大家深入SpringMvc以及排查问题。

关于阅读源码的思路,可参考 -- 如何阅读java源码

使用Spring MVC时,我们常编写一个spring-mvc.xml,xml中添加 <mvc:annotation-driven/> 标签,这个标签是由MvcNamespaceHandler处理。

MvcNamespaceHandler#parse -> AnnotationDrivenBeanDefinitionParser#parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
    ...

    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);    // #1
    ...

    RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);    // #3
    RuntimeBeanReference validator = getValidator(element, source, parserContext);    // #4
    RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);    // #5

    ...

    ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);    // #6
    ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);    // #7
    ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);    // #8
    String asyncTimeout = getAsyncTimeout(element);    
    RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);    
    ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
    ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

    RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);    // #9
    ...
    addRequestBodyAdvice(handlerAdapterDef);    // #10
    addResponseBodyAdvice(handlerAdapterDef);    // #11

    ...
}

这里主要准备一些SpringMvc使用的组件

#1 构造RequestMappingHandlerMapping

#3 处理conversion-service配置,构造ConversionService,默认使用FormattingConversionService处理数字,日期等属性转化

#4 处理validator配置,构造Validator,验证参数

#5 处理message-codes-resolver配置,构造MessageCodesResolver,负责可以绑定错误码和错误信息

#6 处理message-converters配置,构造HttpMessageConverter,负责对http的body进行读写,解析工作,如MappingJackson2XmlHttpMessageConverter负责json格式的http请求。

这里会根据Java环境中存在的json框架,添加对应的json处理类。例如maven引入了jackson框架,就会添加MappingJackson2HttpMessageConverter。

#7 处理argument-resolvers配置,构造HandlerMethodArgumentResolver,负责解析业务方法的参数

#8 处理return-value-handlers配置,构造HandlerMethodReturnValueHandler,负责处理业务方法返回值

#9 构造RequestMappingHandlerAdapter,RequestMappingHandlerAdapter负责使用HandlerMethod调用业务方法,并且处理方法参数和返回,前面步骤获取的messageConverters,argumentResolvers,returnValueHandlers都要注入到RequestMappingHandlerAdapter的属性中,同时,它也会添加默认的HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler等组件类(参考RequestMappingHandlerAdapter#getDefaultReturnValueHandlers)。

#10 如果Java环境中存在jackson框架,构造JsonViewRequestBodyAdvice,JsonViewRequestBodyAdvice可以在读取http请求body前后做一些额外处理

#11 如果Java环境中存在jackson框架,构造JsonViewResponseBodyAdvice,JsonViewResponseBodyAdvice可以在写入http响应body前后做一些额外处理

RequestMappingHandlerMapping维护了URL,@RequestMapping注解以及业务方法(就是@RequestMapping标注的方法)之间的映射关系,父类AbstractHandlerMethodMapping实现了InitializingBean接口,AbstractHandlerMethodMapping#afterPropertiesSet -> initHandlerMethods

protected void initHandlerMethods() {
    ...
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
            getApplicationContext().getBeanNamesForType(Object.class));    // #1

    for (String beanName : beanNames) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                beanType = getApplicationContext().getType(beanName);
            }
            ...
            if (beanType != null && isHandler(beanType)) {    // #2
                detectHandlerMethods(beanName);    // #3
            }
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

#1 获取所有的beanName

#2 调用RequestMappingHandlerMapping#isHandler,检查Class是否有@Controller/@RequestMapping注解

#3 构造并注册HandlerMethod(封装了业务方法和对应的Class)

这里不再深入解析RequestMappingHandlerMapping,有兴趣的同学可以继续阅读源码。

DispatcherServlet

使用SpringMVC需要在web.xml添加一个DispatcherServlet处理的servlet,DispatcherServlet是实现SpringMVC的关键组件类。

DispatcherServlet#initStrategies方法将加载Spring Context的SpringMvc组件类,如MultipartResolver,HandlerMapping,设置为自己的属性。

如initHandlerMappings方法获取AnnotationDrivenBeanDefinitionParser中构造的RequestMappingHandlerMapping,作为自己的属性handlerMappings。

DispatcherServlet的父类FrameworkServlet继承于HttpServletBean,

doGet/doPost -> DispatcherServlet#doService -> doDispatch

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);    // #1
            multipartRequestParsed = (processedRequest != request);

            mappedHandler = getHandler(processedRequest);    // #2
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());    // #3

            String method = request.getMethod();    // #4
            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)) {    // #5
                return;    
            }

            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());    // #6

            if (asyncManager.isConcurrentHandlingStarted()) {    // #7
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);    // #8
        }
        ...
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    // #9
    }
    ...
}

#1 处理multipart类型的请求,如文件上传

#2 主要是通过RequestMappingHandlerMapping#getHandler,查找对应的HandlerMethod,再构造HandlerExecutionChain。

HandlerExecutionChain可以添加Interceptor拦截器,执行额外的流程,Cors请求的处理就是通过Interceptor实现的。

#3 这里使用RequestMappingHandlerAdapter。HandlerAdapter负责使用handler处理requests

#4 处理http的lastModified标识

#5 调用HandlerInterceptor#preHandle

#6 使用HandlerAdapter处理请求

#7 判断是否为异步请求

#8 调用HandlerInterceptor#postHandle

#9 渲染ModelAndView以及处理异常

#6 步骤 -> AbstractHandlerMethodAdapter#handle -> RequestMappingHandlerAdapter#handleInternal -> invokeHandlerMethod ->

ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);    // #1
    setResponseStatus(webRequest);    // #2

    if (returnValue == null) {    // #3
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    try {
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);    // #4
    }
    ...
}

#1 处理请求,注意providedArgs参数是null

#2 设置响应码

#3 处理异常场景

#4 处理业务方法返回结果

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);    // #1
    ...
    Object returnValue = doInvoke(args);    // #2
    ...
    return returnValue;
}

#1 从http请求中组装业务方法的参数

#2 调用业务方法

解析业务方法参数

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    MethodParameter[] parameters = getMethodParameters();    // #1
    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);    // #2
        if (args[i] != null) {
            continue;
        }
        if (this.argumentResolvers.supportsParameter(parameter)) {
            try {
                args[i] = this.argumentResolvers.resolveArgument(
                        parameter, mavContainer, request, this.dataBinderFactory);    // #3
                continue;
            }
            ...
        }
        ...
    }
    return args;
}

#1 获取业务方法参数信息

#2 从providedArgs获取对应的方法参数(providedArgs参数是null,这里都是返回null)

#3 从http请求中解析方法参数

HandlerMethodArgumentResolverComposite#resolveArgument

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);    // #1
    if (resolver == null) {
        throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
    }
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

#1 根据参数的注解,从HandlerMethodArgumentResolverComposite#argumentResolvers选择正确的HandlerMethodArgumentResolver处理参数。

RequestParamMethodArgumentResolver处理@RequestParam注解的参数

PathVariableMethodArgumentResolver处理@PathVariable注解的参数

RequestResponseBodyMethodProcessor处理@RequestBody注解的参数

注意:RequestResponseBodyMethodProcessor调用父类方法AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters方法,该方法会根据HttpReqeust的contentType,查找对应的HttpMessageConverter进行处理。

处理业务方法的结果

ServletInvocableHandlerMethod#invokeAndHandle方法 #4 步骤 -> HandlerMethodReturnValueHandlerComposite#handleReturnValue

public void handleReturnValue(Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);    // #1
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);    // #2
}

#1 根据业务方法注解,执行结果类型等信息,从returnValueHandlers从选择一个HandlerMethodReturnValueHandler

#2 使用HandlerMethodReturnValueHandler处理结果。

RequestResponseBodyMethodProcessor#handleReturnValue负责处理@ResponseBody注解的方法结果,也是通过HttpMessageConverter转化数据,并将转化后的数据写入到http响应中。

HandlerInterceptor

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

DispatcherServlet#doDispatch方法调用了mappedHandler#applyPreHandle, mappedHandler#applyPostHandle,在processDispatchResult方法中会调用mappedHandler#triggerAfterCompletion。

异常处理

DispatcherServlet#processDispatchResult -> processHandlerException

如果抛出的异常是ModelAndViewDefiningException,获取ModelAndView并渲染,否则使用HandlerExceptionResolver进行处理。

SpringMvc默认提供了三种HandlerExceptionResolver实现,

ExceptionHandlerExceptionResolver:查找Spring Context中@ControllerAdvice标注的类,并使用它们@ExceptionHandler标注的方法处理异常。

DefaultHandlerExceptionResolver:处理常见的异常,如业务方法不存放,http请求内容解析失败等

ResponseStatusExceptionResolver:解析@ResponseStatus标注的异常类

DispatcherServlet会加载spring-webmvc.jar下DispatcherServlet.properties配置,获取一些组件接口的默认实现类,HandlerExceptionResolver的默认实现类就在该文件中配置了。

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

Spring Content层次

除了spring-mvc.xml的配置,我们还可以定义一个spring.xml,存放除了非WEB的bean以及配置,然后使用在web.xml中使用ContextLoaderListener加载该配置。

ContextLoaderListener#contextInitialized -> ContextLoader#initWebApplicationContext,这里会构造一个RootSpringContext,并且添加到servletContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中。

FrameworkServlet#initWebApplicationContext也会构造一个WebSpringContext,并将servletContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE指向的SpringContext作为父Content。

注意:WebSpringContext可以使用RootSpringContext的bean,但RootSpringContext不能使用WebSpringContext定义的bean。

SpringBoot中,复用了DispatcherServlet,HttpMessageConverter,RequestMappingHandlerAdapter,RequestMappingHandlerMapping等组件,主要的处理流程并没有变化,后面也写文章解析对应的内容。

如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章