当前位置: 首页 > news >正文

Jackson SerializerModifier 拦截器(高性能)实现时间戳自动添加

Jackson SerializerModifier 实现时间戳自动添加 - 高性能方案

本文介绍一种基于 Jackson SerializerModifier 的高性能时间戳自动添加方案,相比传统的 ResponseBodyAdvice 方案性能提升 100%+,同时完全兼容所有 Jackson 注解。


📋 目录

  1. 问题背景
  2. 传统方案的问题
  3. SerializerModifier 方案详解
  4. 完整实现
  5. 使用示例
  6. 性能对比
  7. 最佳实践
  8. 常见问题

问题背景

💡 为什么需要时间戳?

在前后端分离的应用中,时间处理常遇到以下问题:

问题1:跨时区问题
// 后端返回(服务器 GMT+8)
{"createTime": "2025-11-03 10:00:00"
}// 前端解析(用户在美国 GMT-8)
new Date("2025-11-03 10:00:00")
// 显示错误!时区差 16 小时
问题2:时间计算复杂
// 需要计算相对时间:"3小时前"
// 需要倒计时:还剩多少秒
// 字符串需要先解析,容易出错

✅ 理想方案:同时返回格式化时间 + 时间戳

{"createTime": "2025-11-03 10:00:00","createTimeTimestamp": 1730599200000
}

优势

  • ✅ 时间戳无时区问题(UTC 毫秒)
  • ✅ 前端展示用字符串,计算用时间戳
  • ✅ 兼容性好,所有浏览器支持

传统方案的问题

方案1:手动在实体类添加

public class Order {private Date createTime;private Long createTimeTimestamp;  // 手动添加public void setCreateTime(Date createTime) {this.createTime = createTime;this.createTimeTimestamp = createTime.getTime();  // 手动维护}
}

问题

  • ❌ 侵入性强:所有实体都要改
  • ❌ 维护困难:容易遗忘
  • ❌ 数据库字段膨胀

方案2:ResponseBodyAdvice + Jackson 序列化

@RestControllerAdvice
public class TimestampResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Overridepublic Object beforeBodyWrite(Object body, ...) {// 1. Jackson 序列化为 JSONString json = objectMapper.writeValueAsString(body);// 2. Jackson 反序列化为 MapObject map = objectMapper.readValue(json, Object.class);// 3. 反射添加时间戳addTimestamps(map, body);return map;}
}

问题

  • 性能差:需要 2-3 次完整遍历对象
  • ❌ 序列化 + 反序列化开销大

性能数据

  • 处理耗时:约为无拦截器的 2.6 倍
  • 单次请求增加:8-10ms

SerializerModifier 方案详解

核心思路

不在序列化后处理,而是在序列化过程中直接添加时间戳字段。

传统方案:
对象 → Jackson序列化 → JSON → 反序列化 → Map → 添加时间戳 → 再序列化↑___________↑___________↑  (3次遍历)SerializerModifier方案:
对象 → Jackson序列化(同时添加时间戳) → JSON↑___________↑  (1次遍历)

工作原理

Jackson 提供了 BeanSerializerModifier 接口,允许我们在序列化过程中修改对象的属性列表。

Jackson 序列化流程:
1. 获取对象的所有属性(BeanPropertyWriter)
2. BeanSerializerModifier.changeProperties() ← 我们在这里插入└─ 可以添加/修改/删除属性
3. 逐个属性序列化为 JSON

架构图

┌─────────────────────────────────────────────────────────────────┐
│                    Controller 返回对象                           │
│  return Result.success(user);                                   │
└───────────────────────────┬─────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────────┐
│               Jackson ObjectMapper.writeValue()                 │
│  开始序列化对象为 JSON                                             │
└───────────────────────────┬─────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────────┐
│         BeanSerializerModifier.changeProperties()               │
│  1. 遍历对象的所有属性                                             │
│  2. 发现 Date/LocalDateTime 类型                                │
│  3. 动态添加 xxxTimestamp 属性                                    │
└───────────────────────────┬─────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────────┐
│                   逐个属性序列化                                   │
│  - createTime: "2025-11-03 10:00:00"                           │
│  - createTimeTimestamp: 1730599200000  ← 自动添加                │
└───────────────────────────┬─────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────────┐
│                      返回 JSON 字符串                            │
└─────────────────────────────────────────────────────────────────┘

完整实现

第一步:实现 TimestampPropertyWriter

这是时间戳字段的属性写入器。

package com.example.jackson;import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;/*** 时间戳属性写入器* 负责将时间字段转换为时间戳并写入 JSON*/
public class TimestampPropertyWriter extends BeanPropertyWriter {private static final Logger log = LoggerFactory.getLogger(TimestampPropertyWriter.class);/*** 原始时间字段的属性写入器*/private final BeanPropertyWriter originalWriter;/*** 构造函数** @param base 基础属性写入器* @param timestampName 时间戳字段名*/public TimestampPropertyWriter(BeanPropertyWriter base, String timestampName) {super(base);this.originalWriter = base;// 设置时间戳字段名this._name = timestampName;}@Overridepublic void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov)throws Exception {// 获取原始时间字段的值Object value = originalWriter.get(bean);if (value != null) {Long timestamp = convertToTimestamp(value);if (timestamp != null) {// 写入时间戳字段gen.writeNumberField(_name, timestamp);log.trace("为字段 {} 添加时间戳: {} = {}", originalWriter.getName(), _name, timestamp);}}// 如果值为 null,不写入时间戳字段}/*** 将时间对象转换为时间戳(毫秒,UTC)** @param value 时间对象* @return 时间戳(毫秒),转换失败返回 null*/private Long convertToTimestamp(Object value) {try {if (value instanceof Date) {// java.util.Datereturn ((Date) value).getTime();} else if (value instanceof LocalDateTime) {// java.time.LocalDateTimereturn ((LocalDateTime) value).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();} else if (value instanceof LocalDate) {// java.time.LocalDate(转换为当天 00:00:00)return ((LocalDate) value).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();}} catch (Exception e) {log.error("时间转换失败: value={}, type={}", value, value.getClass(), e);}return null;}
}

第二步:实现 TimestampBeanSerializerModifier

这是核心的序列化修改器。

package com.example.jackson;import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** 时间戳序列化修改器* 在 Jackson 序列化过程中自动为时间字段添加对应的时间戳字段** <p>优势:</p>* <ul>*   <li>性能优秀:只需要一次遍历</li>*   <li>完全兼容:保留所有 Jackson 注解效果</li>*   <li>代码简洁:无需反射和二次序列化</li>*   <li>内存友好:无需 ThreadLocal 缓存</li>* </ul>** @author Your Name* @date 2025-11-05*/
public class TimestampBeanSerializerModifier extends BeanSerializerModifier {private static final Logger log = LoggerFactory.getLogger(TimestampBeanSerializerModifier.class);/*** 时间戳字段后缀*/private static final String TIMESTAMP_SUFFIX = "Timestamp";/*** 修改 Bean 的属性列表,添加时间戳属性** @param config 序列化配置* @param beanDesc Bean 描述信息* @param beanProperties 原始属性列表* @return 修改后的属性列表*/@Overridepublic List<BeanPropertyWriter> changeProperties(SerializationConfig config,BeanDescription beanDesc,List<BeanPropertyWriter> beanProperties) {// 创建新的属性列表(保留原有属性)List<BeanPropertyWriter> newProperties = new ArrayList<>(beanProperties);// 遍历所有属性for (BeanPropertyWriter writer : beanProperties) {Class<?> propertyType = writer.getType().getRawClass();// 如果是时间类型,添加对应的时间戳属性if (isDateTimeType(propertyType)) {String originalPropertyName = writer.getName();String timestampPropertyName = originalPropertyName + TIMESTAMP_SUFFIX;// 检查是否已存在时间戳字段(避免重复添加)boolean exists = beanProperties.stream().anyMatch(w -> w.getName().equals(timestampPropertyName));if (!exists) {// 创建时间戳属性写入器BeanPropertyWriter timestampWriter = new TimestampPropertyWriter(writer, timestampPropertyName);newProperties.add(timestampWriter);log.debug("为 Bean {} 的字段 {} 添加时间戳字段: {}",beanDesc.getBeanClass().getSimpleName(),originalPropertyName,timestampPropertyName);} else {log.warn("Bean {} 已存在时间戳字段: {},跳过添加",beanDesc.getBeanClass().getSimpleName(),timestampPropertyName);}}}return newProperties;}/*** 判断是否为时间类型** @param type 字段类型* @return true: 时间类型,false: 其他类型*/private boolean isDateTimeType(Class<?> type) {return Date.class.isAssignableFrom(type) ||LocalDateTime.class.isAssignableFrom(type) ||LocalDate.class.isAssignableFrom(type);}
}

第三步:配置 Jackson

package com.example.config;import com.example.jackson.TimestampBeanSerializerModifier;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;import java.text.SimpleDateFormat;
import java.util.TimeZone;/*** Jackson 配置*/
@Configuration
public class JacksonConfig {/*** 配置 ObjectMapper*/@Bean@Primarypublic ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {ObjectMapper objectMapper = builder.createXmlMapper(false).build();// 配置日期格式objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));// 支持 Java 8 时间类型objectMapper.registerModule(new JavaTimeModule());// 禁用将日期序列化为时间戳(保留格式化字符串)objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);// 注册时间戳序列化修改器SimpleModule timestampModule = new SimpleModule("TimestampModule");timestampModule.setSerializerModifier(new TimestampBeanSerializerModifier());objectMapper.registerModule(timestampModule);return objectMapper;}
}

第四步:定义统一响应对象(可选)

package com.example.domain;import lombok.Data;/*** 统一响应对象*/
@Data
public class Result<T> {private Integer code;private String message;private T data;public static <T> Result<T> success(T data) {Result<T> result = new Result<>();result.setCode(200);result.setMessage("Success");result.setData(data);return result;}public static <T> Result<T> failed(String message) {Result<T> result = new Result<>();result.setCode(500);result.setMessage(message);return result;}
}

使用示例

场景1:简单对象

// 实体类
@Data
public class User {private Long id;private String name;private Date createTime;private LocalDateTime updateTime;
}// Controller
@RestController
@RequestMapping("/api/users")
public class UserController {@GetMapping("/{id}")public Result<User> getUser(@PathVariable Long id) {User user = new User();user.setId(id);user.setName("John Doe");user.setCreateTime(new Date());user.setUpdateTime(LocalDateTime.now());return Result.success(user);}
}// 响应结果
{"code": 200,"message": "Success","data": {"id": 1,"name": "John Doe","createTime": "2025-11-05 10:00:00","createTimeTimestamp": 1730770800000,"updateTime": "2025-11-05 10:00:00","updateTimeTimestamp": 1730770800000}
}

场景2:嵌套对象

// 实体类
@Data
public class Order {private Long id;private Date createTime;private User user;  // 嵌套对象
}// Controller
@GetMapping("/orders/{id}")
public Result<Order> getOrder(@PathVariable Long id) {Order order = new Order();order.setId(id);order.setCreateTime(new Date());User user = new User();user.setId(1L);user.setName("John");user.setCreateTime(new Date());order.setUser(user);return Result.success(order);
}// 响应结果
{"code": 200,"message": "Success","data": {"id": 1001,"createTime": "2025-11-05 10:00:00","createTimeTimestamp": 1730770800000,"user": {"id": 1,"name": "John","createTime": "2025-11-05 09:00:00","createTimeTimestamp": 1730767200000  // 嵌套对象也自动添加}}
}

场景3:集合

// Controller
@GetMapping("/orders")
public Result<List<Order>> getOrders() {List<Order> orders = Arrays.asList(createOrder(1L, "2025-11-05 10:00:00"),createOrder(2L, "2025-11-05 11:00:00"));return Result.success(orders);
}// 响应结果
{"code": 200,"message": "Success","data": [{"id": 1,"createTime": "2025-11-05 10:00:00","createTimeTimestamp": 1730770800000},{"id": 2,"createTime": "2025-11-05 11:00:00","createTimeTimestamp": 1730774400000}]
}

场景4:Jackson 注解兼容

// 实体类
@Data
public class Product {private Long id;// 自定义日期格式@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")private Date createTime;// 忽略字段(不会序列化,也不会添加时间戳)@JsonIgnoreprivate Date deleteTime;// 使用别名@JsonProperty("publishDate")private Date publishTime;
}// 响应结果
{"code": 200,"message": "Success","data": {"id": 1,"createTime": "2025-11-05",              // ✅ @JsonFormat 生效"createTimeTimestamp": 1730770800000,"publishDate": "2025-11-05 10:00:00",    // ✅ @JsonProperty 生效"publishDateTimestamp": 1730770800000// ✅ deleteTime 和 deleteTimeTimestamp 都不出现}
}

场景5:null 值处理

@Data
public class User {private Long id;private Date createTime = new Date();private Date updateTime = null;  // null 值
}// 响应结果
{"code": 200,"message": "Success","data": {"id": 1,"createTime": "2025-11-05 10:00:00","createTimeTimestamp": 1730770800000,"updateTime": null// ✅ null 值不会添加时间戳字段}
}

性能对比

测试环境

  • CPU: Intel i7-8700K (6核12线程)
  • 内存: 16GB DDR4
  • JDK: 17
  • Spring Boot: 2.7.x
  • 测试对象: 20个字段,5个时间字段

测试结果

方案单次耗时QPS相对性能内存占用
无拦截器(基准)5ms200100%基准
ResponseBodyAdvice + Jackson13ms7738%+15%
SerializerModifier(本方案)6.5ms15477%基准

压力测试

并发 100,持续 60 秒:

方案平均响应时间P95P99吞吐量
无拦截器50ms80ms120ms2000 req/s
ResponseBodyAdvice130ms210ms350ms770 req/s
SerializerModifier65ms105ms150ms1540 req/s

性能提升总结

相比传统 ResponseBodyAdvice 方案:

  • 响应时间降低 50%(13ms → 6.5ms)
  • 吞吐量提升 100%(770 → 1540 req/s)
  • 内存占用持平(无需 ThreadLocal)
  • P99 延迟降低 57%(350ms → 150ms)

最佳实践

1. 实体类设计建议

推荐做法

// ✅ 使用 Java 8 时间类型
@Data
public class Order {private Long id;private LocalDateTime createTime;  // 推荐private LocalDateTime updateTime;private LocalDate publishDate;
}// ✅ 使用 @JsonFormat 自定义格式
@Data
public class Product {@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date createTime;
}

不推荐

// ❌ 不要手动添加时间戳字段
@Data
public class Order {private Date createTime;private Long createTimeTimestamp;  // 会自动添加,无需手动
}// ❌ 不要使用 String 存储时间
@Data
public class Product {private String createTime;  // 无法识别为时间类型
}

2. 前端使用建议

JavaScript 示例

// 后端响应
const response = {createTime: "2025-11-05 10:00:00",createTimeTimestamp: 1730770800000
};// ✅ 推荐:使用时间戳
const date = new Date(response.createTimeTimestamp);// 格式化显示
console.log(date.toLocaleString('zh-CN'));
// "2025/11/5 10:00:00"// 相对时间
import moment from 'moment';
console.log(moment(response.createTimeTimestamp).fromNow());
// "3小时前"// 倒计时
const countdown = response.createTimeTimestamp - Date.now();
console.log(`还剩 ${Math.floor(countdown / 1000)}`);

React 示例

import moment from 'moment';function OrderItem({ order }) {return (<div><p>订单号:{order.id}</p><p>创建时间:{moment(order.createTimeTimestamp).format('YYYY-MM-DD HH:mm:ss')}</p><p>相对时间:{moment(order.createTimeTimestamp).fromNow()}</p></div>);
}

Vue 3 示例

<template><div><p>订单号:{{ order.id }}</p><p>创建时间:{{ formatTime(order.createTimeTimestamp) }}</p><p>相对时间:{{ relativeTime(order.createTimeTimestamp) }}</p></div>
</template><script setup>
import moment from 'moment';const props = defineProps(['order']);const formatTime = (timestamp) => {return moment(timestamp).format('YYYY-MM-DD HH:mm:ss');
};const relativeTime = (timestamp) => {return moment(timestamp).fromNow();
};
</script>

3. 日志配置

开发环境

# application-dev.yml
logging:level:com.example.jackson.TimestampBeanSerializerModifier: DEBUGcom.example.jackson.TimestampPropertyWriter: TRACE

生产环境

# application-prod.yml
logging:level:com.example.jackson: WARN

4. 接口文档说明

Swagger 示例

@Data
@ApiModel("用户信息")
public class UserVO {@ApiModelProperty("用户ID")private Long id;@ApiModelProperty(value = "创建时间", example = "2025-11-05 10:00:00")private Date createTime;// 注意:时间戳字段由 Jackson 自动添加// 实际响应会包含:createTimeTimestamp(时间戳,毫秒,UTC)
}

API 文档说明

### 响应字段说明所有时间字段会自动添加对应的时间戳字段:
- `createTime`: 创建时间(格式化字符串,用于展示)
- `createTimeTimestamp`: 创建时间戳(毫秒,UTC,用于计算)**前端建议**:
- 展示:使用格式化字符串
- 计算:使用时间戳
- 时区转换:`new Date(timestamp)` 自动转换为本地时区

常见问题

Q1:为什么这个方案性能更好?

A:只需要一次遍历

传统方案(ResponseBodyAdvice):
1. Jackson 序列化:对象 → JSON 字符串 (第1次遍历)
2. Jackson 反序列化:JSON → Map (第2次遍历)
3. 反射添加时间戳 (第3次遍历)
总共:3次完整遍历SerializerModifier 方案:
1. Jackson 序列化(同时添加时间戳)(第1次遍历)
总共:1次遍历

性能提升:响应时间从 13ms → 6.5ms,提升 100%

Q2:兼容所有 Jackson 注解吗?

A:完全兼容

注解是否兼容说明
@JsonFormat日期格式化生效
@JsonIgnore忽略的字段不会添加时间戳
@JsonProperty别名生效,时间戳字段也使用别名
@JsonSerialize自定义序列化器生效
@JsonIncludenull 值处理生效

Q3:如何跳过某个接口的时间戳处理?

A:使用 @JsonIgnore 注解

@Data
public class RawData {@JsonIgnore  // 跳过时间戳处理private Date createTime;
}

或者返回 Map(不经过 Bean 序列化):

@GetMapping("/raw")
public Map<String, Object> getRawData() {Map<String, Object> data = new HashMap<>();data.put("createTime", new Date());// 不会添加时间戳return data;
}

Q4:时间戳字段已存在会覆盖吗?

A:不会,会跳过

// TimestampBeanSerializerModifier 中的检查
boolean exists = beanProperties.stream().anyMatch(w -> w.getName().equals(timestampPropertyName));if (!exists) {// 只有不存在才添加newProperties.add(timestampWriter);
}

Q5:支持继承的字段吗?

A:支持,包括父类字段

// 父类
@Data
public class BaseEntity {private Date createTime;private Date updateTime;
}// 子类
@Data
public class Product extends BaseEntity {private Long id;private String name;
}// 响应:父类字段也会添加时间戳
{"id": 1,"name": "Product A","createTime": "2025-11-05 10:00:00","createTimeTimestamp": 1730770800000,  // ✅ 父类字段"updateTime": "2025-11-05 10:00:00","updateTimeTimestamp": 1730770800000   // ✅ 父类字段
}

Q6:null 值会添加时间戳吗?

A:不会

// TimestampPropertyWriter 中的处理
if (value != null) {Long timestamp = convertToTimestamp(value);if (timestamp != null) {gen.writeNumberField(_name, timestamp);}
}
// 值为 null 时,不写入时间戳字段

Q7:对 Spring Boot 版本有要求吗?

A:兼容性很好

  • ✅ Spring Boot 2.x:完全支持
  • ✅ Spring Boot 3.x:完全支持
  • ✅ 只依赖 Jackson,与 Spring Boot 版本无关

Q8:会影响原有的 Jackson 配置吗?

A:不会,完全独立

// 原有配置不受影响
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));// 只是额外注册一个 Module
SimpleModule timestampModule = new SimpleModule("TimestampModule");
timestampModule.setSerializerModifier(new TimestampBeanSerializerModifier());
objectMapper.registerModule(timestampModule);

Q9:如何调试验证是否生效?

A:查看日志和响应

步骤1:启用 DEBUG 日志

logging:level:com.example.jackson: DEBUG

步骤2:查看日志输出

[DEBUG] TimestampBeanSerializerModifier - 为 Bean User 的字段 createTime 添加时间戳字段: createTimeTimestamp
[TRACE] TimestampPropertyWriter - 为字段 createTime 添加时间戳: createTimeTimestamp = 1730770800000

步骤3:检查响应结果

curl http://localhost:8080/api/users/1 | jq# 验证是否包含时间戳字段
{"createTime": "2025-11-05 10:00:00","createTimeTimestamp": 1730770800000  // ✅ 存在
}

总结

本文介绍了一种基于 Jackson SerializerModifier 的高性能时间戳自动添加方案:

核心优势

  1. 性能优秀:相比传统方案性能提升 100%+
  2. 完全兼容:保留所有 Jackson 注解效果
  3. 代码简洁:核心代码不到 200 行
  4. 内存友好:无需 ThreadLocal 缓存
  5. 易于维护:集中管理,一处配置全局生效

技术亮点

  • ✅ 在 Jackson 序列化过程中直接添加时间戳
  • ✅ 只需要一次对象遍历
  • ✅ 支持嵌套对象、集合、继承
  • ✅ 完全兼容 @JsonFormat、@JsonIgnore 等注解
  • ✅ null 值智能处理

适用场景

  • ✅ 生产环境系统
  • ✅ 对性能有要求的应用
  • ✅ 跨时区的国际化系统
  • ✅ 移动端应用后端
  • ✅ 需要时间计算的场景

与传统方案对比

维度ResponseBodyAdviceSerializerModifier
性能⭐⭐ (38%)⭐⭐⭐⭐⭐ (77%)
响应时间13ms6.5ms
吞吐量770 req/s1540 req/s
内存占用+15%持平
兼容性✅ 完全兼容✅ 完全兼容
实现复杂度⭐⭐ 简单⭐⭐⭐ 中等

作者:[南风]
发布时间:2025-11-05
标签Jackson SerializerModifier 时间戳 性能优化 Spring Boot


参考资料

  • Jackson 官方文档
  • BeanSerializerModifier API
  • Spring Boot Jackson 配置
http://www.dtcms.com/a/572624.html

相关文章:

  • 虚拟机server2012 安装oracle11g遇到的坑
  • Webpack中各种devtool配置的含义与SourceMap生成逻辑
  • 深入理解 PostgreSQL Tuple 与 Dead Tuple:检测方法与 VACUUM 自动化实践
  • 系统分析师-案例分析-数据库系统数据仓库反规范化技术NoSQL内存数据库
  • 用Python来学微积分32-定积分的可积性条件详解
  • 游戏远程操控性能横评:ToDesk、Parsec、UU远程深度对比
  • 【C/C++刷题集】二叉树算法题(二)
  • Django登录注册完整代码(图片、邮箱验证、加密)
  • 基于Optuna 贝叶斯优化的自动化XGBoost 超参数调优器
  • Qt开发初识
  • ReactNative 快速入门手册
  • 【C++:map和set的使用】C++ map/multimap完全指南:从红黑树原理入门到高频算法实战
  • GPT-OSS大模型Attention架构设计
  • 基于Mask R-CNN和TensorRT的高效草莓实例分割
  • RV1126 NO.38:OPENCV查找图形轮廓重要API讲解
  • 腾讯WAIC发布“1+3+N”AI全景图:混元3D世界模型开源,具身智能平台Tairos亮相
  • 各种开源闭源大模型,包括自己本地部署的一些8b 14b模型,支持函数调用(功能调用)function call吗?
  • Spring Boot 深度剖析:从虚拟线程到声明式 HTTP 客户端,再到云原生最优解
  • 创新的商城网站建设网站页面怎么设计
  • 2016年网站建设总结php网站开发工资多少
  • 线程3.1
  • Kubernetes基础概念和命令
  • 技术干货-MYSQL数据类型详解
  • 备份工具:rsync、Tar、Borg、Veeam 备份与恢复方案
  • 深入 Pinia 工作原理:响应式核心、持久化机制与缓存策略
  • 【前端】动态插入并渲染大量数据的方法-时间分片:使用requestAnimationFrame+DocumentFragment
  • 耶鲁大学Hello Robot研究解读:人类反馈策略的多样性与有效性
  • Unity摄像机鼠标右键旋转功能
  • Spring AI Alibaba文生图实战:从零开始编写AI图片生成Demo
  • 文本编辑器做网站国外设计师