SpringBoot request processing flow

1. Introduction

The SpringBoot request processing process is mainly divided into four parts: request distribution, mapping processor, and calling processor method.

2. Request distribution

Whenever SpringBoot receives an interface request, it first enters the DispatcherServlet #doService method of tomcat, and distributes the request through doDispatch.

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
    // set attribute to request
    try {
    
    
        doDispatch(request, response);
    }
    finally {
    
    
    }
}

3. Mapping Processor

The first step in request dispatch is to map handlers. Traverse the handlerMappings through the getHandler method, and obtain the request handler through the HandlerMapping. For the current interface request, RequestMappingHandlerMapping is used. After getting the processor object, you can call the interface method.

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

There are 5 mappers supported by default in handlerMappings, all of which are generated when the project starts.

Fourth, call the processor method

When calling a handler object, the handler object will first be forced into a handler method object HandlerMethod. Next, call the request handler method. In-depth discovery through breakpoints, the main workflow is as follows:

ServletInvocableHandlerMethod#invokeAndHandle ->
InvocableHandlerMethod#invokeForRequest

Call the handler method through invokeAndHandle in ServletInvocableHandlerMethod, get the return value and write it into response. Among them, invokeForRequest of the parent class InvocableHandlerMethod will be called. Get the parameter list of the request in invokeForRequest.

Get parameter list

In-depth discovery through breakpoints, the request parameter list is obtained through 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);
}

In getMethodArgumentValues, each parameter will be traversed, trying to get the parser for parsing.

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

The parameter parsing is to obtain the supported parsers by traversing the argumentResolvers of the context, and cache the parsers, so that the subsequent parameter parsers can be obtained directly from the cache.

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

The default 26 parameter parsers are as follows. This request uses the RequestResponseBodyMethodProcessor parser.

After getting the parser, the following is to parse the parameters. The parsing of parameters parses the text into corresponding parameter objects through 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);
}

The parameter parsing process is to traverse each parameter and obtain a suitable parser to parse the parameters. The program will cache the parameters and the corresponding parser, and the specific parsing details are implemented by MessageConverter.

call handler method

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

get return result

Obtaining the return result first parses the type of the return value, and obtains the supported return value handlers by traversing the returnValueHandlers in the context.

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

The default supported return value handlers are as follows:
insert image description here

After getting the return value handler, call the handleReturnValue method to write the return result into the response object, and the writing details still depend on the MessageConverter support.

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

The workflow for obtaining the returned result is to obtain the appropriate return value handler, then call the return value handler method, and write the result into the response object through the MessageConverter.

V. Summary

The general workflow of the SpringBoot interface method:

  1. Servlet interface distribution
  2. Mapping handler method
    Get the HandlerMapping of the context, and map the handler method according to the request parameters.
  3. Call the processor method
    a. Parse the parameters through the parameter parser of the context, and the specific parsing details depend on the MessageConverter.
    b. The call handler method references the call method.
    c. Process the return value through the return result processor of the context, and rely on MessageConverter to write the return value into the response object.

Guess you like

Origin blog.csdn.net/ChineseSoftware/article/details/126674470