Loading

SpringBoot接口方法调用过程分析

前言

本文主要通过调试的方法来分析 SpringBoot 接口方法的大致调用过程,从调用流程层面简单地分析下源码,不进行深入解读。此外,本文调试使用的是 SpringBoot 的 2.2.2.RELEASE 版本。

调用流程

SpringBoot 接口方法的调用流程主要分为四部分:请求分发、映射处理器、调用处理器方法。下面具体分析大致工作流程。

请求分发

每当 SpringBoot 收到接口请求后,首先就是进入 Tomcat 的DispatcherServlet#doService方法,通过doDispatch进行请求分发。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // set attribute to request

    try {
        doDispatch(request, response);
    }
    finally {
    }
}

映射处理器

请求分发的第一步就是映射处理器。通过getHandler方法遍历handlerMappings,通过HandlerMapping获取请求处理器。对于当前接口请求采用的是RequestMappingHandlerMapping。拿到处理器对象后,就可以调用接口方法。

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

            // Determine handler for the current request.
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
    }
}

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

handlerMappings中默认支持5个映射器,它们都是在项目启动时生成的。

image

调用处理器方法

调用处理器对象时,首先会将处理器对象强转为处理器方法对象HandlerMethod。接着,调用请求处理器方法。通过断点深入发现,主要工作流程如下:

ServletInvocableHandlerMethod#invokeAndHandle -> InvocableHandlerMethod#invokeForRequest

在ServletInvocableHandlerMethod中通过invokeAndHandle调用处理器方法,获取返回值写入response。其中,又会调用父类InvocableHandlerMethod的invokeForRequest。在invokeForRequest中去获取请求的参数列表。

获取参数列表

通过断点深入发现,获取请求参数列表是通过InvocableHandlerMethod#invokeForRequest

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    // 获取请求参数
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    return doInvoke(args);
}

在getMethodArgumentValues中,会遍历每一个参数,尝试获取解析器进行解析。

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

    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }

    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            // 解析每一个参数
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        catch (Exception ex) {
            throw ex;
        }
    }
    return args;
}

参数的解析是通过遍历上下文的argumentResolvers来获取支持的解析器,并将解析器缓存,这样后面的参数解析器就可以直接从缓存中获取。

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 获取解析器
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    // 解析参数
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        // 获取支持的解析器,并写入缓存
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            if (resolver.supportsParameter(parameter)) {
                result = resolver;
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}

默认的26个参数解析器如下,本次请求采用的是RequestResponseBodyMethodProcessor解析器。

image

获取解析器之后,下面就是对参数进行解析。参数的解析通过MessageConverter将文本解析成相应的参数对象。

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

    parameter = parameter.nestedIfOptional();
    // 读取MessageConverter解析结果
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);
    return adaptArgumentIfNecessary(arg, parameter);
}

总结而言,参数的解析流程就是遍历每一个参数,获取合适的解析器对参数进行解析。程序会缓存参数和对应的解析器,而具体的解析细节依赖MessageConverter来实现。

调用处理器方法

protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        // 调用处理器方法
        return getBridgedMethod().invoke(getBean(), args);
    }
    catch (IllegalArgumentException ex) {
    }
}

获取返回结果

获取返回结果首先会解析返回值的类型,通过遍历上下文中的returnValueHandlers获取支持的返回值处理器。

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

默认支持的返回值处理器如下:
image

拿到返回值处理器后,调用handleReturnValue方法将返回结果写入响应对象中,写入细节还是依赖MessageConverter支持。

// handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // Try even with null return value. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

总结而言,获取返回结果的工作流程是获取合适的返回值处理器,然后调用返回值处理器方法,将结果通过MessageConverter写入到响应对象中。

总结

以上介绍了 SpringBoot 接口方法的大致工作流程:
1、Servlet接口分发
2、映射处理器方法
获取上下文的HandlerMapping,根据请求参数映射处理器方法。
3、调用处理器方法
(1)通过上下文的参数解析器解析参数,具体解析细节依赖MessageConverter。
(2)调用处理器方法引用调用方法。
(3)通过上下文的返回结果处理器处理返回值,依赖MessageConverter将返回值写入响应对象中。

posted @ 2022-05-06 12:37  flowers-bloom  阅读(3056)  评论(0编辑  收藏  举报