SpringBoot 響應(yīng) json 數(shù)據(jù)的后端源碼處理流程
今天微信群里一位網(wǎng)友拋出了一個問題,我這里花幾分鐘時間給大家嘮叨嘮叨。
我們先回憶一下,在 Spring Boot 中,處理 JSON 響應(yīng)的流程通常涉及的幾個代碼開發(fā)步驟。
-
定義數(shù)據(jù)模型:首先,你需要定義一個或多個 Java 類來表示你想要序列化或反序列化的數(shù)據(jù)。這些類通常使用 @Getter和@Setter注解來自動生成 getter 和 setter 方法,或者使用 Lombok 庫來簡化代碼。
public class User {
private String name;
private int age;
// Getters and setters...
}
-
配置 Spring Boot 應(yīng)用程序:確保你的 application.properties或application.yml文件中包含了正確的配置,以便 Spring Boot 能夠自動配置 JSON 處理。默認情況下,Spring Boot 配置了 Jackson 或者 Gson 作為 JSON 序列化和反序列化的工具。
# application.properties
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
-
創(chuàng)建控制器:創(chuàng)建一個或多個控制器(Controller)來處理 HTTP 請求。在控制器中,你可以定義路由(endpoints)和處理方法(handler methods),這些方法會返回 JSON 響應(yīng)。
@RestController
public class UserController {
@GetMapping("/users")
public List<User> getAllUsers() {
// ... 獲取用戶列表
return users;
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
// ... 創(chuàng)建用戶
return user;
}
}
-
使用注解處理請求和響應(yīng):在控制器方法中,使用
@GetMapping、@PostMapping等注解來處理不同類型的 HTTP 請求。使用@RequestBody注解來接收 JSON 格式的請求體,使用@ResponseBody或@RestController注解來指示方法的返回值應(yīng)該被序列化為 JSON。 -
序列化和反序列化:Spring Boot 會自動處理 JSON 的序列化和反序列化。當你返回一個對象時,Spring Boot 會使用配置的 JSON 序列化器(如 Jackson)將其轉(zhuǎn)換為 JSON 字符串。當客戶端發(fā)送 JSON 數(shù)據(jù)時,Spring Boot 會使用 JSON 反序列化器將 JSON 字符串轉(zhuǎn)換為 Java 對象。
-
處理異常:在開發(fā)過程中,可能會遇到數(shù)據(jù)驗證失敗、資源找不到等異常情況。你可以使用
@ExceptionHandler注解來處理這些異常,并返回適當?shù)?HTTP 狀態(tài)碼和 JSON 響應(yīng)體。
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleRuntimeException(RuntimeException ex) {
return ex.getMessage();
}
-
測試:最后,編寫單元測試和集成測試來確保你的控制器方法按預(yù)期工作,并且返回的 JSON 數(shù)據(jù)是正確的。
通過以上步驟,我們就可以在 Spring Boot 應(yīng)用程序中處理 JSON 響應(yīng)。當然,這里面有些步驟不是必須的。
下面我們再來一起看看,SpringBoot 框架在響應(yīng) json 數(shù)據(jù)時的處理流程。
先看一個示例代碼:
訪問 localhost:8080/jsonTest —— 返回 json 格式的數(shù)據(jù)
@Controller
public class ResponseTestController {
@ResponseBody // 標注 —— 自動返回json數(shù)據(jù)
@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)
設(shè)置以下幾處斷點:
step into —— 返回值處理器邏輯
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
先找返回值處理器
HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);
執(zhí)行下列代碼,找到符合要求的返回值處理器
在眾多返回值處理器中找到符合要求的 —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;
}
調(diào)用返回值處理器(interface HandlerMethodReturnValueHandler)處理:
判斷是否支持這種類型的返回值,支持調(diào)用返回值處理器,調(diào)用 handleReturnValue 進行處理
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter var1);
void handleReturnValue(@Nullable Object var1, MethodParameter var2, ModelAndViewContainer var3, NativeWebRequest var4) throws Exception;
}
RequestResponseBodyMethodRrocessor工作原理
關(guān)鍵代碼:
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);
}
處理返回值的方法
使用消息轉(zhuǎn)換器(涉及內(nèi)容協(xié)商機制,請移步下文內(nèi)容協(xié)商機制)進行寫出操作,消息轉(zhuǎn)換器工作過程如下:
?
瀏覽器告知服務(wù)器其處理能力 服務(wù)器根據(jù)其自身能力,判斷服務(wù)器能生產(chǎn)什么樣的內(nèi)容 SpringMVC 遍歷所有容器的底層 HttpMessageConverter, 看誰能處理
內(nèi)容協(xié)商機制
1. 內(nèi)容協(xié)商: 瀏覽器告知服務(wù)器其需要什么類型的服務(wù)類型
瀏覽器可接受的類型:
服務(wù)器得到瀏覽器的處理能力:
服務(wù)器將返回內(nèi)容轉(zhuǎn)為瀏覽器能處理的形式
2. 內(nèi)容協(xié)商原理重點源代碼
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消息轉(zhuǎn)換器原理
-
MessageConverter 規(guī)范
-
默認的MessageConverter
-
最終 MappingJackson2HttpMessageConverter 把對象轉(zhuǎn)為JSON(利用底層的jackson的objectMapper轉(zhuǎn)換的)
總結(jié)
?
返回值處理器判斷是否支持這種類型的返回值( supportsReturnType) 返回值處理器調(diào)用handleReturnValue 進行處理 以 @ResponseBody 注解為例,RequestResponseBodyMethodProcessor 可以處理返回值標 最后,利用 MessageConverters 進行處理 將數(shù)據(jù)寫為json
這個處理流程 debug 起來并不難,難在不少人想不到 debug 方法。以上,大致記住哦,面試中會被問到的。
