SpringBoot 请求处理流程

一、简介

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个映射器,它们都是在项目启动时生成的。

四、调用处理器方法

调用处理器对象时,首先会将处理器对象强转为处理器方法对象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解析器。

获取解析器之后,下面就是对参数进行解析。参数的解析通过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;
}

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

拿到返回值处理器后,调用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. 调用处理器方法
    a. 通过上下文的参数解析器解析参数,具体解析细节依赖MessageConverter。
    b. 调用处理器方法引用调用方法。
    c. 通过上下文的返回结果处理器处理返回值,依赖MessageConverter将返回值写入响应对象中。
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 是基于 Spring Framework 的快速开发框架,它可以帮助开发者快速构建 Web 应用程序。在 Spring Boot 中,请求处理是通过控制器(Controller)实现的。 控制器是一个带有 @Controller 注解的类,它包含处理 HTTP 请求的方法。每个方法都使用特定的请求路径和 HTTP 方法(GET、POST、PUT、DELETE 等)进行映射。例如,以下代码段显示了一个控制器类的示例: ``` @Controller @RequestMapping("/user") public class UserController { @GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { // 处理获取用户信息的逻辑 User user = userService.getUserById(id); return ResponseEntity.ok(user); } @PostMapping("/") public ResponseEntity<User> createUser(@RequestBody User user) { // 处理创建用户的逻辑 User newUser = userService.createUser(user); return ResponseEntity.ok(newUser); } } ``` 在这个示例中,我们定义了一个名为 UserController 的控制器类,并使用 @RequestMapping 注解指定了该控制器的基本请求路径为 "/user"。然后,我们定义了两个方法,一个用于获取用户信息,另一个用于创建用户。这些方法使用 @GetMapping 和 @PostMapping 注解分别指定了 GET 和 POST 请求的路径和方法类型。 在这些方法中,我们可以通过参数获取请求中的数据,例如在 getUserById 方法中,我们使用 @PathVariable 注解获取请求路径中的参数 id。在 createUser 方法中,我们使用 @RequestBody 注解获取请求体中的数据,并将其转换为 User 对象。 最后,这些方法会返回一个 ResponseEntity 对象,该对象包含响应数据和 HTTP 状态码。在这个例子中,我们使用 ResponseEntity.ok() 方法返回一个 HTTP 状态码为 200(OK)的响应,并将获取或创建的用户对象作为响应数据返回。 这就是 Spring Boot请求处理的基本流程。开发者只需要编写控制器类和处理方法,Spring Boot 就会自动将请求路由到正确的方法,并处理响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值