避免前端(JavaScript)處理大數(如 Long、BigInteger)時發生精度丟失問題,所以引入了自定義 Jackson 配置。
先看代碼:
/** 根據id修改員工信息*/@PutMappingpublic R<String> update(HttpServletRequest request,@RequestBody Employee employee){log.info(employee.toString());Long empId = (Long)request.getSession().getAttribute("employee");employee.setUpdateTime(LocalDateTime.now());employee.setUpdateUser(empId);employeeService.updateById(employee);return R.success("員工信息修改成功");}
這里由于要修改的員工信息的id是通過mp雪花算法得到的超長數字,js前端在訪問這個數據的時候會出現精度損失,導致后端拿不到這個id,因此無法更新數據
1. jackson 是什么?
Jackson 是一個功能強大的 Java 類庫,主要用于在 Java 對象 和 JSON 數據之間做轉換。
它可以:
把 Java 對象 轉成 JSON 字符串(序列化)
把 JSON 字符串 解析成 Java 對象(反序列化)
你可以把 Jackson 理解為 Java 世界里的 “JSON翻譯器”。
官網地址:https://github.com/FasterXML/jackson
在 Java 里常用的 JSON 處理庫有:
Jackson (最流行)
Gson (Google出的,也挺常見)
Fastjson (阿里出的,國內有些公司用)
其中 Jackson 在 Spring Boot 里默認就是集成的(不用特地引)。
這里我們用json來處理
2. jackson 和 json 是什么關系?
JSON(JavaScript Object Notation) 是一種數據交換格式,本身跟 Jackson 沒有直接關系。
Jackson 是處理 JSON 的工具,是幫你在 Java 中讀寫 JSON 的 實現庫。
換句話說,JSON 是標準,Jackson 是工具。
就像:“水(JSON)是資源,桶(Jackson)是工具”,你用 Jackson 來搬運、轉換 JSON 數據。
為什么要特別處理 Long / BigInteger?
這個非常關鍵!
原因是 JavaScript 的 number 類型(雙精度浮點數)在 2^53(大約 16位整數)之后就會失真。
在前端(特別是Vue、React)里,如果后端直接返回數字格式的 Long 或 BigInteger,前端 JSON.parse() 后就精度丟了!
所以你要在后端 把這些大整數轉成字符串輸出,前端才能安全處理,比如:
{"orderId": "9223372036854775807"
}
前端拿到字符串后,自己解析或展示,不會丟精度!
因此我們要創建自定義模塊來注冊,序列化器,反序列化器
自定義Jackson ObjectMapper
SimpleModule simpleModule = new SimpleModule()
序列化器
這個是反序列化器(json->java對象):
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
這里針對 Java 8 時間類型(LocalDateTime、LocalDate、LocalTime)指定了解析格式。
例如,遇到 “2025-04-28 12:00:00” 這樣的字符串時,能正確反序列化成 LocalDateTime。
反序列化器
接著這里用反序列化器(java對象->json):
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
將 BigInteger 和 Long 類型序列化為字符串(防止前端 JavaScript 解析大整數丟失精度問題)。
將 LocalDateTime、LocalDate、LocalTime 使用指定格式序列化成字符串。
jackson整體關系類圖
jacksonObjectMapper結構圖
擴展mvc架構的消息轉換器
前面知識配置了jackson的信息,但是還沒有完成實現,由于后端發給前端的信息的json格式的,而包裝發送json數據是mvc設置的,所以我們還需要在mvc配置類中加入擴展mvc架構信息轉換器
具體代碼如下:
/** 擴展mvc框架的消息轉換器* */@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {//創建消息轉換器對象MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();//設置對象轉換器,底層使用jackson將java轉成jsonmessageConverter.setObjectMapper(new JacksonObjectMapper());//將上面的消息轉換器對象追加到mvc框架的轉換器集合中converters.add(0,messageConverter);}
但是這里由于我們擴展了 SpringMVC 配置,導致 Spring Boot 自動配置失效了。 我們繼承了一個 MVC 配置類,打破了默認的靜態資源映射規則,在 Spring Boot 中(比如用 spring-boot-starter-web):
默認情況下,Spring Boot 自動幫你配置好靜態資源訪問路徑,比如:
/static/
/public/
/resources/
/META-INF/resources/
只要把 HTML、CSS、JS 放在 static 里,可以直接通過 URL 訪問,無需自己寫 addResourceHandlers()。但是!! 一旦手動繼承了 SpringMVC 配置,即使你只是重寫 extendMessageConverters(),Spring Boot會認為你要接管整個SpringMVC配置!
于是,Spring Boot默認的靜態資源映射失效了。
重寫靜態資源映射就可以了:
/** 設置靜態資源映射*/@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {log.info("開始進行靜態資源映射...");registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/static/backend/");registry.addResourceHandler("/front/**").addResourceLocations("classpath:/static/front/");}