• 数据库连接池


    一、简介

    SpringBoot 请求处理流程主要分为四部分:请求分发、映射处理器、调用处理器方法。

    二、请求分发

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

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // set attribute to request
        try {
            doDispatch(request, response);
        }
        finally {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、映射处理器

    请求分发的第一步就是映射处理器。通过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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

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

    四、调用处理器方法

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

    ServletInvocableHandlerMethod#invokeAndHandle ->
    InvocableHandlerMethod#invokeForRequest
    
    • 1
    • 2

    在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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    参数的解析是通过遍历上下文的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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

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

    获取解析器之后,下面就是对参数进行解析。参数的解析通过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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    调用处理器方法

    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            // 调用处理器方法
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    获取返回结果

    获取返回结果首先会解析返回值的类型,通过遍历上下文中的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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    默认支持的返回值处理器如下:
    在这里插入图片描述

    拿到返回值处理器后,调用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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    五、总结

    SpringBoot 接口方法的大致工作流程:

    1. Servlet接口分发
    2. 映射处理器方法
      获取上下文的HandlerMapping,根据请求参数映射处理器方法。
    3. 调用处理器方法
      a. 通过上下文的参数解析器解析参数,具体解析细节依赖MessageConverter。
      b. 调用处理器方法引用调用方法。
      c. 通过上下文的返回结果处理器处理返回值,依赖MessageConverter将返回值写入响应对象中。
  • 相关阅读:
    面试突击:什么是粘包和半包?怎么解决?
    Linux ALSA源码分析(基于Linux 5.18)
    ClickHouse 使用技巧总结
    XSStrike工具使用说明
    JavaScript 日常开发的 9 个实用代码片段 (part 1)
    企业网站受到攻击会有什么影响
    JavaWeb复习
    【权限管理项目总结】SpringBoot+MyBatis+Shiro+EhCache+Thymeleaf:编码+功能测试+知识点
    Shiro 权限绕过漏洞(CVE-2020-1957)
    【MindSpore易点通】在开发环境中如何使用MindInsight在线调试器
  • 原文地址:https://blog.csdn.net/ChineseSoftware/article/details/126674470