Appearance
Spring MVC Message Converters 深度解析 🚀
什么是 Message Converters?为什么需要它?
想象一下,你正在开发一个 RESTful API,客户端发送 JSON 数据,你的 Controller 需要接收 Kotlin 对象;同时,你的 Controller 返回 Kotlin 对象,客户端期望收到 JSON 响应。这个"魔法般"的转换过程就是由 HttpMessageConverter 完成的!
NOTE
HttpMessageConverter 是 Spring MVC 中负责在 HTTP 请求/响应与 Java/Kotlin 对象之间进行序列化和反序列化的核心组件。
核心痛点与解决方案
kotlin
@RestController
class UserController {
@PostMapping("/users")
fun createUser(request: HttpServletRequest): ResponseEntity<String> {
// 😰 手动读取请求体
val json = request.reader.readText()
// 😰 手动解析 JSON
val objectMapper = ObjectMapper()
val user = objectMapper.readValue(json, User::class.java)
// 业务逻辑处理...
val savedUser = userService.save(user)
// 😰 手动转换为 JSON 响应
val responseJson = objectMapper.writeValueAsString(savedUser)
return ResponseEntity.ok(responseJson)
}
}
kotlin
@RestController
class UserController {
@PostMapping("/users")
fun createUser(@RequestBody user: User): User {
// ✅ 自动将 JSON 转换为 User 对象
// ✅ 自动将返回的 User 对象转换为 JSON
return userService.save(user)
}
}
Message Converters 的工作原理
自定义 Message Converters 配置
基础配置示例
kotlin
@Configuration
class WebConfiguration : WebMvcConfigurer {
override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
// 创建自定义的 Jackson 配置
val builder = Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(SimpleDateFormat("yyyy-MM-dd"))
.modulesToInstall(ParameterNamesModule())
// 添加 JSON 转换器
converters.add(MappingJackson2HttpMessageConverter(builder.build()))
// 添加 XML 转换器
converters.add(MappingJackson2XmlHttpMessageConverter(
builder.createXmlMapper(true).build()
))
}
}
TIP
使用 configureMessageConverters()
会完全替换默认的转换器,而使用 extendMessageConverters()
则是在默认转换器基础上添加或修改。
扩展现有转换器(推荐方式)
kotlin
@Configuration
class WebConfiguration : WebMvcConfigurer {
override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
// 🎯 在现有转换器基础上进行扩展
val jacksonConverter = converters
.filterIsInstance<MappingJackson2HttpMessageConverter>()
.firstOrNull()
jacksonConverter?.let { converter ->
val objectMapper = converter.objectMapper
// 自定义 ObjectMapper 配置
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
}
}
}
实际业务场景应用
场景一:API 版本兼容性处理
kotlin
data class UserV1(
val id: Long,
val name: String,
val email: String
)
data class UserV2(
val id: Long,
val fullName: String, // 字段名变更
val email: String,
val createdAt: LocalDateTime // 新增字段
)
@Configuration
class ApiVersionConfiguration : WebMvcConfigurer {
override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
// 为不同 API 版本配置不同的转换策略
val versionTolerantMapper = ObjectMapper().apply {
// 忽略未知属性,保证向后兼容
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// 忽略空值属性
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
converters.add(0, MappingJackson2HttpMessageConverter(versionTolerantMapper))
}
}
场景二:多格式数据支持
kotlin
@RestController
class DataController {
@PostMapping("/data", consumes = ["application/json", "application/xml"])
fun processData(@RequestBody data: ProcessRequest): ProcessResponse {
// ✅ 同时支持 JSON 和 XML 格式的请求
return dataService.process(data)
}
@GetMapping("/data/{id}", produces = ["application/json", "application/xml"])
fun getData(@PathVariable id: Long): DataResponse {
// ✅ 根据客户端 Accept 头返回对应格式
return dataService.findById(id)
}
}
Spring Boot 中的最佳实践
IMPORTANT
在 Spring Boot 应用中,推荐使用 HttpMessageConverters
机制或 extendMessageConverters
方法,而不是完全替换默认配置。
Spring Boot 自动配置增强
kotlin
@Configuration
class MessageConverterConfiguration {
@Bean
@Primary
fun customObjectMapper(): ObjectMapper {
return Jackson2ObjectMapperBuilder()
.simpleDateFormat("yyyy-MM-dd HH:mm:ss")
.serializers(LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
.deserializers(LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
.modules(
ParameterNamesModule(), // Java 8 参数名支持
Jdk8Module(), // Optional 等 Java 8 类型支持
JavaTimeModule() // Java 8 时间类型支持
)
.build()
.apply {
// 自定义配置
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
}
}
}
常见问题与解决方案
问题 1:日期格式处理
WARNING
默认情况下,Jackson 会将日期序列化为时间戳,这可能不符合前端期望。
kotlin
// 解决方案:全局日期格式配置
@Configuration
class DateFormatConfiguration : WebMvcConfigurer {
override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
converters.filterIsInstance<MappingJackson2HttpMessageConverter>()
.forEach { converter ->
converter.objectMapper.apply {
// 禁用时间戳格式
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
// 设置日期格式
dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
}
}
}
}
问题 2:循环引用处理
kotlin
data class Department(
val id: Long,
val name: String,
@JsonManagedReference
val employees: List<Employee>
)
data class Employee(
val id: Long,
val name: String,
@JsonBackReference
val department: Department
)
Jackson 模块生态系统
Spring Boot 会自动检测并注册以下常用模块:
模块 | 功能 | 使用场景 |
---|---|---|
jackson-datatype-jsr310 | Java 8 日期时间 API | LocalDateTime , LocalDate 等 |
jackson-datatype-jdk8 | Java 8 类型支持 | Optional , OptionalInt 等 |
jackson-module-kotlin | Kotlin 支持 | Kotlin 数据类、空安全等 |
jackson-module-parameter-names | 参数名支持 | 构造函数参数绑定 |
完整的自定义配置示例
kotlin
@Configuration
class ComprehensiveMessageConverterConfig : WebMvcConfigurer {
override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
// 1. 配置 JSON 转换器
val jsonBuilder = Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.modulesToInstall(
ParameterNamesModule(),
Jdk8Module(),
JavaTimeModule(),
KotlinModule.Builder().build()
)
.featuresToDisable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
)
.featuresToEnable(
JsonParser.Feature.ALLOW_COMMENTS,
JsonParser.Feature.ALLOW_SINGLE_QUOTES
)
converters.add(MappingJackson2HttpMessageConverter(jsonBuilder.build()))
// 2. 配置 XML 转换器
val xmlMapper = jsonBuilder.createXmlMapper(true).build()
converters.add(MappingJackson2XmlHttpMessageConverter(xmlMapper))
// 3. 添加字符串转换器(处理纯文本)
val stringConverter = StringHttpMessageConverter(StandardCharsets.UTF_8)
stringConverter.supportedMediaTypes = listOf(
MediaType.TEXT_PLAIN,
MediaType.TEXT_HTML,
MediaType.APPLICATION_JSON // 支持返回原始 JSON 字符串
)
converters.add(stringConverter)
}
}
总结 🎉
HttpMessageConverter 是 Spring MVC 中的"翻译官",它让我们能够:
✅ 自动转换:无需手动处理 JSON/XML 与对象的转换
✅ 类型安全:强类型的 Kotlin 对象,编译时检查
✅ 格式灵活:支持多种数据格式(JSON、XML、自定义格式)
✅ 配置简单:通过配置类轻松自定义转换行为
TIP
在实际项目中,建议优先使用 extendMessageConverters()
方法来扩展默认配置,这样既能保持 Spring Boot 的自动配置优势,又能满足项目的特殊需求。
通过合理配置 Message Converters,你的 Spring Boot 应用将能够优雅地处理各种数据格式转换需求,为构建高质量的 RESTful API 奠定坚实基础! 🎯