You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在未知子类时反序列化实现接口的枚举(Spring Boot Starter)

解决Spring Boot Starter中BaseErrorCode枚举的通用反序列化问题

核心思路

既然确定所有BaseErrorCode的实现都是枚举类,我们可以在Starter中实现一个通用反序列化器,通过扫描classpath自动收集所有实现BaseErrorCode的枚举类型,反序列化时根据JSON中的字符串值匹配对应的枚举实例,完全不需要提前知晓具体的枚举实现类。

实现步骤

1. 编写通用反序列化器

这个反序列化器会接收扫描到的所有枚举类型,反序列化时遍历这些枚举,找到与JSON值匹配的实例:

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.deser.std.StdDeserializer

class BaseEnumCodeDeserializer(private val enumTypes: List<Class<out Enum<*>>>) : StdDeserializer<BaseErrorCode>(BaseErrorCode::class.java) {
    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BaseErrorCode {
        val codeValue = p.text
        // 遍历所有枚举类,查找匹配的枚举实例
        return enumTypes.asSequence()
            .map { it.enumConstants as Array<BaseErrorCode> }
            .flatMap { it.asSequence() }
            .firstOrNull { it.name == codeValue }
            ?: throw ctxt.weirdStringException(
                codeValue, 
                BaseErrorCode::class.java, 
                "找不到匹配的错误码枚举值: '$codeValue'"
            )
    }
}

2. 在Starter中配置Jackson模块并扫描枚举类

通过ClassPathScanningCandidateComponentProvider扫描classpath下所有实现BaseErrorCode的枚举类,注册到Jackson的反序列化器中:

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter
import org.springframework.core.type.filter.AssignableTypeFilter
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilderCustomizer

@Configuration
class ErrorCodeJacksonConfig {

    @Bean
    fun baseErrorCodeDeserializer(): BaseEnumCodeDeserializer {
        val scanner = ClassPathScanningCandidateComponentProvider(false)
        // 过滤实现BaseErrorCode的类
        scanner.addIncludeFilter(AssignableTypeFilter(BaseErrorCode::class.java))
        // 进一步过滤出枚举类
        scanner.addIncludeFilter(object : AbstractTypeHierarchyTraversingFilter(false, false) {
            override fun matchClassName(className: String): Boolean {
                return try {
                    val clazz = Class.forName(className)
                    clazz.isEnum && BaseErrorCode::class.java.isAssignableFrom(clazz)
                } catch (e: ClassNotFoundException) {
                    false
                }
            }
        })

        // 扫描所有包下的符合条件的枚举类
        val enumTypes = scanner.findCandidateComponents("")
            .map { Class.forName(it.beanClassName) as Class<out Enum<*>> }

        return BaseEnumCodeDeserializer(enumTypes)
    }

    @Bean
    fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer {
        return Jackson2ObjectMapperBuilderCustomizer { builder ->
            // 为BaseErrorCode类型注册自定义反序列化器
            builder.deserializerByType(BaseErrorCode::class.java, baseErrorCodeDeserializer())
        }
    }
}

方案优势

  • 仅需修改Starter代码,使用该Starter的仓库无需任何额外配置或修改
  • 自动适配所有实现BaseErrorCode的枚举类,无需提前知晓具体枚举实现
  • 保持原有序列化格式不变(JSON中错误码仍为枚举name,如"FOO")

注意事项

  • 确保各仓库中自定义错误码枚举的name全局唯一,避免出现不同枚举类有相同name导致匹配错误的情况
  • 扫描范围是整个classpath,若需限制扫描包路径,可将scanner.findCandidateComponents("")中的空字符串改为指定包前缀(但需用户配置,不符合"无需修改各仓库"的要求,因此不推荐)

内容的提问来源于stack exchange,提问作者FeXseven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:27:52