SpringBoot 响应 json 数据的后端源码处理流程

业余草

共 10291字,需浏览 21分钟

 · 2024-04-12

今天微信群里一位网友抛出了一个问题,我这里花几分钟时间给大家唠叨唠叨。

我们先回忆一下,在 Spring Boot 中,处理 JSON 响应的流程通常涉及的几个代码开发步骤。

  1. 定义数据模型:首先,你需要定义一个或多个 Java 类来表示你想要序列化或反序列化的数据。这些类通常使用 @Getter@Setter 注解来自动生成 getter 和 setter 方法,或者使用 Lombok 库来简化代码。
public class User {
   private String name;
   private int age;

   // Getters and setters...
}
  1. 配置 Spring Boot 应用程序:确保你的 application.propertiesapplication.yml 文件中包含了正确的配置,以便 Spring Boot 能够自动配置 JSON 处理。默认情况下,Spring Boot 配置了 Jackson 或者 Gson 作为 JSON 序列化和反序列化的工具。
# application.properties
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
  1. 创建控制器:创建一个或多个控制器(Controller)来处理 HTTP 请求。在控制器中,你可以定义路由(endpoints)和处理方法(handler methods),这些方法会返回 JSON 响应。
@RestController
public class UserController {

   @GetMapping("/users")
   public List<User> getAllUsers() {
       // ... 获取用户列表
       return users;
   }

   @PostMapping("/users")
   public User createUser(@RequestBody User user) {
       // ... 创建用户
       return user;
   }
}
  1. 使用注解处理请求和响应:在控制器方法中,使用 @GetMapping@PostMapping 等注解来处理不同类型的 HTTP 请求。使用 @RequestBody 注解来接收 JSON 格式的请求体,使用 @ResponseBody@RestController 注解来指示方法的返回值应该被序列化为 JSON。

  2. 序列化和反序列化:Spring Boot 会自动处理 JSON 的序列化和反序列化。当你返回一个对象时,Spring Boot 会使用配置的 JSON 序列化器(如 Jackson)将其转换为 JSON 字符串。当客户端发送 JSON 数据时,Spring Boot 会使用 JSON 反序列化器将 JSON 字符串转换为 Java 对象。

  3. 处理异常:在开发过程中,可能会遇到数据验证失败、资源找不到等异常情况。你可以使用 @ExceptionHandler 注解来处理这些异常,并返回适当的 HTTP 状态码和 JSON 响应体。

@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleRuntimeException(RuntimeException ex
{
   return ex.getMessage();
}
  1. 测试:最后,编写单元测试和集成测试来确保你的控制器方法按预期工作,并且返回的 JSON 数据是正确的。

通过以上步骤,我们就可以在 Spring Boot 应用程序中处理 JSON 响应。当然,这里面有些步骤不是必须的。

下面我们再来一起看看,SpringBoot 框架在响应 json 数据时的处理流程。

先看一个示例代码:

访问 localhost:8080/jsonTest —— 返回 json 格式的数据

@Controller
public class ResponseTestController {
    @ResponseBody // 标注 —— 自动返回json数据
    @GetMapping("/jsonTest")
    public Person testPerson(){
        Person person = new Person();
        person.setAge(20);
        person.setUserName("Liuwanqing");
        Pet pet = new Pet();
        pet.setName("huahua");
        pet.setAge("五个月");
        person.setPet(pet);
        return person;
    }
}

返回值解析原理

SpringBoot 支持的返回值类型是由返回值解析器决定的,SpringBoot 返回值类型如下:

ModelAndView
Model
View

SpringBoot共含 15 种返回值解析器决定了其支持 15 种返回值:

源代码分析(debug)

设置以下几处断点:

step into —— 返回值处理器逻辑

this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);

先找返回值处理器

HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);

执行下列代码,找到符合要求的返回值处理器

在众多返回值处理器中找到符合要求的 —RequestResponseBodyMethodRrocessor
即 RequestResponseBodyMethodRrocessor 可处理标注了 @ResponseBody 注解的返回值

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    boolean isAsyncValue = this.isAsyncReturnValue(value, returnType);
    Iterator var4 = this.returnValueHandlers.iterator();

    HandlerMethodReturnValueHandler handler;
    do {
        do {
            if (!var4.hasNext()) {
                return null;
            }

            handler = (HandlerMethodReturnValueHandler)var4.next();
        } while(isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler));
    } while(!handler.supportsReturnType(returnType));

    return handler;
}

调用返回值处理器(interface HandlerMethodReturnValueHandler)处理:

判断是否支持这种类型的返回值,支持调用返回值处理器,调用 handleReturnValue 进行处理

public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter var1);

    void handleReturnValue(@Nullable Object var1, MethodParameter var2, ModelAndViewContainer var3, NativeWebRequest var4) throws Exception;
}

RequestResponseBodyMethodRrocessor工作原理

关键代码:

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

处理返回值的方法

使用消息转换器(涉及内容协商机制,请移步下文内容协商机制)进行写出操作,消息转换器工作过程如下:

  1. 浏览器告知服务器其处理能力
  2. 服务器根据其自身能力,判断服务器能生产什么样的内容
  3. SpringMVC 遍历所有容器的底层 HttpMessageConverter, 看谁能处理

内容协商机制

1. 内容协商: 浏览器告知服务器其需要什么类型的服务类型

浏览器可接受的类型:

服务器得到浏览器的处理能力:

服务器将返回内容转为浏览器能处理的形式

2. 内容协商原理重点源代码

protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
    Set<MediaType> mediaTypes = (Set)request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    if (!CollectionUtils.isEmpty(mediaTypes)) {
        return new ArrayList(mediaTypes);
    } else if (this.allSupportedMediaTypes.isEmpty()) {
        return Collections.singletonList(MediaType.ALL);
    } else {
        List<MediaType> result = new ArrayList();
        Iterator var6 = this.messageConverters.iterator();

        while(true) {
            while(var6.hasNext()) {
                HttpMessageConverter<?> converter = (HttpMessageConverter)var6.next();
                if (converter instanceof GenericHttpMessageConverter && targetType != null) {
                    if (((GenericHttpMessageConverter)converter).canWrite(targetType, valueClass, (MediaType)null)) {
                        result.addAll(converter.getSupportedMediaTypes());
                    }
                } else if (converter.canWrite(valueClass, (MediaType)null)) {
                    result.addAll(converter.getSupportedMediaTypes());
                }
            }

            return result;
        }
    }
}

HttpMessageConverters消息转换器原理

  1. MessageConverter 规范
  1. 默认的MessageConverter
  1. 最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)

总结

  1. 返回值处理器判断是否支持这种类型的返回值( supportsReturnType)
  2. 返回值处理器调用handleReturnValue 进行处理
  3. 以 @ResponseBody 注解为例,RequestResponseBodyMethodProcessor 可以处理返回值标
  4. 最后,利用 MessageConverters 进行处理 将数据写为json

这个处理流程 debug 起来并不难,难在不少人想不到 debug 方法。以上,大致记住哦,面试中会被问到的。

浏览 4
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报