SpringMVC 之 自定义Body参数解析


此方式仅仅对 @RequestBody 方式的请求生效,对 parame 方式传参并不生效(?key=value&key=value),比如 Get 请求

GET 请求参数配置请参考: 

Java8 时间自定义系列化 与 反系列化:

public class Java8TimeModule extends SimpleModule {
    public Java8TimeModule() {
        super(PackageVersion.VERSION);
    // ======================= 时间序列化规则 ===============================
    // yyyy-MM-dd HH:mm:ss
        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    // yyyy-MM-dd
        this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    // HH:mm:ss
        this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
 
    // ======================= 时间反序列化规则 ==============================
    // yyyy-MM-dd HH:mm:ss
        this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    // yyyy-MM-dd
        this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    // HH:mm:ss
        this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
    }
}

将自定义的系列化规则配置到服务中:(本文页底和自定义枚举系列化一起)


自定义枚举系列化:
1、抽象约束枚举接口:

@JsonDeserialize(using = IEnumDeserializer.class)
public interface IEnumextends Serializable> extends Serializable {
    IRuntimeException EXCEPTION = new IRuntimeException("操作不允许");
    /**
     * 获取枚举编码
     * @return
     */
    int getCode();
    /**
     * 获取枚举值
     * @return
     */
    default T getValue() {
        throw EXCEPTION;
    }
    /**
     * 获取枚举名称
     * @return
     */
    default String getName() {
        return this.name();
    }
    /**
     * 获取枚举名称
     * @return
     */
    String name();
    /**
     * 根据枚举名称获取枚举对象
     * @param clazz
     * @param name
     * @param 
     * @return
     */
    @SneakyThrows
    static extends Enum & IEnum> E sourceOf(Class clazz, String name) {
        Method valuesMethod = clazz.getMethod("values");
        E[] enumInstanceArr = (E[]) valuesMethod.invoke(null);
        for (E enumInstance : enumInstanceArr) {
            if (Objects.equals(enumInstance.name(), name)) {
                return enumInstance;
            }
        }
        throw ExceptionUtil.create(IRuntimeException.class, ErrorResult.PARAMETER_ERROR);
    }
 
    /**
     * 根据编码获取枚举对象
     * @param clazz
     * @param code
     * @param 
     * @return
     */
    @SneakyThrows
    static extends Enum & IEnum> E codeOf(Class clazz, int code) {
        Method valuesMethod = clazz.getMethod("values");
        E[] enumInstanceArr = (E[]) valuesMethod.invoke(null);
        for (E enumInstance : enumInstanceArr) {
            if (code == enumInstance.getCode()) {
                return enumInstance;
            }
        }
        throw ExceptionUtil.create(IRuntimeException.class, ErrorResult.PARAMETER_ERROR);
    }
}

具体枚举类:

public enum SexEnum implements IEnum {
    /**
     * 男性
     */
    MALE(1, "男"),
    /**
     * 女性
     */
    FEMALE(2, "女"),
    /**
     * 未知
     */
    UNKNOWN(3, "未知");
    /**
     * 枚举编码
     */
    @EnumValue
    private int code;
    /**
     * 枚举值
     */
    private String value;
}

手动编写反系列化器(根据自己的业务需求自行编写逻辑)

public class IEnumDeserializer extends JsonDeserializer {
    @Override
    public IEnum deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException {
        JsonNode node = jp.getCodec().readTree(jp);
        String currentName = jp.currentName();
        Object currentValue = jp.getCurrentValue();
        @SuppressWarnings("rawtypes")
        Class enumClazz = BeanUtils.findPropertyType(currentName, currentValue.getClass());
 
        IEnum iEnum;
        if (node instanceof IntNode) {
            // 当客户端传值为枚举的 code 的值得时候
            iEnum = (IEnum) IEnum.codeOf(enumClazz, node.asInt());
        } else if (node instanceof TextNode) {
            // 当客户端传值为 枚举的 name 时候
            iEnum = (IEnum) IEnum.sourceOf(enumClazz, node.asText());
        } else {
            // 若传参的是枚举对象时, JsonNode 为 ObjectNode 类,枚举对象的数据放在一个 LinkedHashMap 中,只需要从 Map 中读取出 name 即可
            iEnum = (IEnum) IEnum.sourceOf(enumClazz, node.get("name").asText());
        }
        return iEnum;
    }
}

将反系列化器注册:

public class IEnumModule extends SimpleModule {
    public IEnumModule() {
        super(PackageVersion.VERSION);
        // 自定义通用枚举类反系列化处理
        this.addDeserializer(IEnum.class, new IEnumDeserializer());
    }
}

转换器配置注册(注册方式二选一即可)

1、不推荐

public class IEnumDeserializerConfig implements ApplicationListener {
    private boolean isExecute = true;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isExecute) {
            ApplicationContext applicationContext = event.getApplicationContext();
            //注册转换器
            ObjectMapper bean = applicationContext.getBean(ObjectMapper.class);
            bean.registerModule(new IEnumModule());
        }
        isExecute = false;
    }
}

2、推荐:

@Configuration
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class JacksonConfig {
    private static final String ASIA_SHANGHAI = "Asia/Shanghai";
    /**
     * 处理 @RequestBody 方式传参的自定义转换
     * java8 时间系列化/反系列化
     */
    @Bean
    @ConditionalOnMissingBean
    public Jackson2ObjectMapperBuilderCustomizer java8Customizer() {
        return (Jackson2ObjectMapperBuilder builder) -> {
            builder.locale(Locale.CHINA);
            builder.timeZone(TimeZone.getTimeZone(ASIA_SHANGHAI));
            builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
            builder.modules(new Java8TimeModule(), new IEnumModule());
        };
    }
}