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

共 10291字,需浏览 21分钟

 ·

2024-04-12 00:15

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

我们先回忆一下,在 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 方法。以上,大致记住哦,面试中会被问到的。

浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

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

手机扫一扫分享

分享
举报