Hessian 4.0.65 支持 JDK8 时间类型序列化/反序列化

原创 临窗旋墨 2025-08-25 阅读:1463 分类: java后端 专题: 架构设计 标签: 序列化,架构

Hessian 4.0.65 支持 JDK8 时间类型序列化/反序列化

源码地址:/lcxm-common - JavaTimeSerializerFactory

20250825

背景

Hessian 4.0.65 默认不支持 JDK 8 时间类型 (LocalDateLocalDateTimeLocalTime) 的序列化/反序列化。原始实现使用单独的 SerializerFactory,重复代码多,不支持泛型对象,也不方便扩展其他时间类型。


改进方案:JavaTimeSerializerFactory

统一工厂 JavaTimeSerializerFactory,特性:

  • 默认支持:LocalDateLocalDateTimeLocalTimeOffsetDateTimeZonedDateTimeYearMonth
  • 可扩展注册其他时间类型
  • 支持对象和泛型对象中的时间类型
  • 缓存 SerializerDeserializer,提高性能

核心设计

1. JavaTimeHandler

封装时间类型的解析和格式化逻辑:

复制代码
public class JavaTimeHandler<T> {
    final Class<T> type;
    final DateTimeFormatter formatter;
    final Function<String, T> parser;
    final Function<T, String> formatterFunc;

    public JavaTimeHandler(Class<T> type,
                           DateTimeFormatter formatter,
                           Function<String, T> parser,
                           Function<T, String> formatterFunc) { ... }

    public String format(Object obj) { return formatterFunc.apply(type.cast(obj)); }
    public T parse(String str) { return parser.apply(str); }
}

2. JavaTimeSerializer

序列化为 Hessian Map 结构:

复制代码
public class JavaTimeSerializer<T> extends AbstractSerializer {
    private static final String KEY = "_value";
    private final JavaTimeHandler<T> handler;

    public JavaTimeSerializer(JavaTimeHandler<T> handler) { this.handler = handler; }

    @Override
    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
        String dateStr = handler.format(obj);
        out.writeMapBegin(handler.type.getName());
        out.writeString(KEY);
        out.writeString(dateStr);
        out.writeMapEnd();
    }
}

3. JavaTimeDeserializer

反序列化 Hessian Map 为时间对象:

复制代码
public class JavaTimeDeserializer<T> extends AbstractDeserializer {
    private static final String KEY = "_value";
    private final JavaTimeHandler<T> handler;

    public JavaTimeDeserializer(JavaTimeHandler<T> handler) { this.handler = handler; }

    @Override
    public Object readMap(AbstractHessianInput in) throws IOException {
        String dateStr = null;
        while (!in.isEnd()) {
            String key = in.readString();
            if (KEY.equals(key)) {
                dateStr = in.readString();
            } else {
                in.readObject();
            }
        }
        in.readMapEnd();
        return dateStr == null ? null : handler.parse(dateStr);
    }

    @Override
    public Class<?> getType() { return handler.type; }
}

4. JavaTimeSerializerFactory

统一管理 JDK8 时间类型的 Hessian 序列化逻辑:

复制代码
public class JavaTimeSerializerFactory extends AbstractSerializerFactory {
    private static final JavaTimeSerializerFactory instance = new JavaTimeSerializerFactory();
    public static JavaTimeSerializerFactory getInstance() { return instance; }

    private final Map<Class<?>, JavaTimeHandler<?>> handlers = new HashMap<>();
    private final Map<Class<?>, Serializer> serializerCache = new HashMap<>();
    private final Map<Class<?>, Deserializer> deserializerCache = new HashMap<>();

    private JavaTimeSerializerFactory() { registerDefaults(); }

    private void registerDefaults() {
        // LocalDate
        register(LocalDate.class, DateTimeFormatter.ISO_LOCAL_DATE,
                str -> LocalDate.parse(str, DateTimeFormatter.ISO_LOCAL_DATE),
                date -> date.format(DateTimeFormatter.ISO_LOCAL_DATE));
        // LocalDateTime
        register(LocalDateTime.class, DateTimeFormatter.ISO_LOCAL_DATE_TIME,
                str -> LocalDateTime.parse(str, DateTimeFormatter.ISO_LOCAL_DATE_TIME),
                dt -> dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        // LocalTime
        register(LocalTime.class, DateTimeFormatter.ISO_LOCAL_TIME,
                str -> LocalTime.parse(str, DateTimeFormatter.ISO_LOCAL_TIME),
                t -> t.format(DateTimeFormatter.ISO_LOCAL_TIME));
        // OffsetDateTime
        register(OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME,
                str -> OffsetDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME),
                dt -> dt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
        // ZonedDateTime
        register(ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME,
                str -> ZonedDateTime.parse(str, DateTimeFormatter.ISO_ZONED_DATE_TIME),
                dt -> dt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
        // YearMonth
        register(YearMonth.class, DateTimeFormatter.ofPattern("yyyy-MM"),
                str -> YearMonth.parse(str, DateTimeFormatter.ofPattern("yyyy-MM")),
                ym -> ym.format(DateTimeFormatter.ofPattern("yyyy-MM")));
    }

    public <T> void register(Class<T> clazz,
                             DateTimeFormatter formatter,
                             Function<String, T> parser,
                             Function<T, String> formatterFunc) {
        handlers.put(clazz, new JavaTimeHandler<>(clazz, formatter, parser, formatterFunc));
    }

    @Override
    public Serializer getSerializer(Class cl) {
        return serializerCache.computeIfAbsent(cl, c -> {
            JavaTimeHandler<?> handler = handlers.get(c);
            return handler != null ? new JavaTimeSerializer<>(handler) : null;
        });
    }

    @Override
    public Deserializer getDeserializer(Class cl) {
        return deserializerCache.computeIfAbsent(cl, c -> {
            JavaTimeHandler<?> handler = handlers.get(c);
            return handler != null ? new JavaTimeDeserializer<>(handler) : null;
        });
    }
}

Hessian2Serializer 工具类

封装 Hessian 序列化/反序列化逻辑:

复制代码
public class Hessian2Serializer implements XqdSerializer {
    public static final SerializerFactory serializerFactory = SerializerFactory.createDefault();

    static {
        serializerFactory.addFactory(JavaTimeSerializerFactory.getInstance());
    }

    @Override
    public <T> byte[] serialize(T obj) {
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            Hessian2Output out = new Hessian2Output(os);
            out.setSerializerFactory(serializerFactory);
            out.writeObject(obj);
            out.flush();
            return os.toByteArray();
        } catch (Exception e) {
            throw new CommonException("Hessian2 序列化失败", e);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        try (ByteArrayInputStream is = new ByteArrayInputStream(data)) {
            Hessian2Input in = new Hessian2Input(is);
            in.setSerializerFactory(serializerFactory);
            return (T) in.readObject(clazz);
        } catch (Exception e) {
            throw new CommonException("Hessian2 反序列化失败", e);
        }
    }

    @Override
    public boolean selfDescribed() { return true; }
}

使用示例

测试单个 LocalDate

复制代码
LocalDate date = LocalDate.now();
byte[] bytes = serializer.serialize(date);
LocalDate result = serializer.deserialize(bytes, LocalDate.class);
System.out.println(result);

测试对象包含 LocalDate

复制代码
TestModel model = new TestModel();
model.setDate(LocalDate.now());

byte[] bytes = serializer.serialize(model);
TestModel result = serializer.deserialize(bytes, TestModel.class);
System.out.println(result.getDate());

测试泛型对象 XqdResponse

复制代码
XqdResponse<LocalDate> resp = XqdResponse.success(LocalDate.now());
byte[] bytes = serializer.serialize(resp);
XqdResponse result = serializer.deserialize(bytes, XqdResponse.class);
System.out.println(result.getData());

序列化/反序列化流程

序列化流程:

  • LocalDate(2025-08-25) →
  • Hessian 流 = {type:"java.time.LocalDate", "_value":"2025-08-25"}

反序列化流程:

  • Hessian 读到 type=java.time.LocalDate → 找到 LocalDateDeserializer
  • 数据结构是 map → 调用 readMap()
  • 在 readMap() 里拿到 "_value" -> "2025-08-25" → 返回 LocalDate.parse("2025-08-25")

优点

  • 支持 JDK8 日期类型:LocalDateLocalDateTimeLocalTimeOffsetDateTimeZonedDateTimeYearMonth
  • 支持对象和泛型对象
  • 可扩展注册新类型
  • 缓存序列化器/反序列化器,提高性能
  • 命名清晰、结构整洁

注意事项

  • 避免使用旧的 LocalDateSerializerFactoryLocalDateTimeSerializerFactory
  • 可通过 JavaTimeSerializerFactory.register() 扩展新类型
  • 使用 Hessian2Serializer 时确保已注册工厂

评论区

avatar
未登录

暂无评论,来发表第一条评论吧~