springmvc+hessian 的LocaldateTime 序列化问题

    xiaoxiao2022-06-30  88

    写在前面:

         阿里代码规范说自从jdk8之后,可以用Localdatetime取代calder,instant 取代date,所以我就信了这个邪,在自己建的实体类里面用了这个LocalDatetime,如下:

    Student.java

    package com.equaker.model; import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; import org.springframework.format.annotation.DateTimeFormat; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; @TableName(value="student") @Data public class Student extends Model<Student> implements Serializable { /** * */ @TableField(exist=false) private static final long serialVersionUID = 3605577030483006663L; @TableId(value="id",type=IdType.AUTO) private Integer id; @TableField(value="name") private String name; @TableField(value="grade") private Integer grade; @TableField(exist = false) private List<Course> courses; @TableField(value="create_time") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; }

    在简单不过的代码了,刚开始是因为前段传参的时候传递形如 "2019-05-22 11:11:11"这样的时间,但是spring说转换错误 string->localdateTime error,所以在属性上面加了@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")。so  一切接受正常。一般的mvc模式 都没问题了。但是在现在微服务流行的形况下(hessian,dubbo等),序列化成为比较普遍的问题。这里主要介绍hessian。

    案例:

    springmvc+hessian

    api层:

    DemoService.java:

    public interface DemoService { void gg(LocalDateTime localDateTime); }

    web层

    先放送两个序列化locldatetime需要用的的工具:

    LocalDateTimeSerialize.java:

    package com.equaker.server.config; import java.io.IOException; import java.time.LocalDateTime; import java.time.ZoneOffset; import com.caucho.hessian.io.AbstractHessianOutput; import com.caucho.hessian.io.AbstractSerializer; public class LocalDateTimeSerializer extends AbstractSerializer { @Override public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { if (obj == null) { out.writeNull(); } else { Class cl = obj.getClass(); if (out.addRef(obj)) { return; } // ref 返回-2 便是开始写Map int ref = out.writeObjectBegin(cl.getName()); if (ref < -1) { out.writeString("value"); Long milliSecond = ((LocalDateTime) obj).toInstant(ZoneOffset.of("+8")).toEpochMilli(); out.writeUTCDate(milliSecond); out.writeMapEnd(); } else { if (ref == -1) { out.writeInt(1); out.writeString("value"); out.writeObjectBegin(cl.getName()); } Long milliSecond = ((LocalDateTime) obj).toInstant(ZoneOffset.of("+8")).toEpochMilli(); out.writeUTCDate(milliSecond); } } } }

    LocalDateTimeDeserializer.java:

    package com.equaker.server.config; import java.io.IOException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import com.caucho.hessian.io.AbstractDeserializer; import com.caucho.hessian.io.AbstractHessianInput; import com.caucho.hessian.io.IOExceptionWrapper; public class LocalDateTimeDeserializer extends AbstractDeserializer{ @Override public Class getType() { return LocalDateTime.class; } @Override public Object readObject(AbstractHessianInput in, Object []fields) throws IOException { String []fieldNames = (String []) fields; int ref = in.addRef(null); long initValue = Long.MIN_VALUE; for (int i = 0; i < fieldNames.length; i++) { String key = fieldNames[i]; if (key.equals("value")) { initValue = in.readUTCDate(); } else { in.readObject(); } } Object value = create(initValue); in.setRef(ref, value); return value; } @Override public Object readMap(AbstractHessianInput in) throws IOException { Map map = new HashMap(); in.addRef(map); while (! in.isEnd()) { map.put(in.readObject(), in.readObject()); } in.readEnd(); return map; } private Object create(long initValue) throws IOException { if (initValue == Long.MIN_VALUE) { throw new IOException(LocalDateTime.class + " expects name."); } try { return LocalDateTime.ofEpochSecond(new Long(initValue)/1000,Integer.valueOf(String.valueOf(initValue00))*1000,ZoneOffset.of("+8")); } catch (Exception e) { throw new IOExceptionWrapper(e); } } }

    HessianConfig.java:

    package com.equaker.server.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.caucho.hessian.io.ExtSerializerFactory; import com.caucho.hessian.io.SerializerFactory; @Configuration public class HessianConfig { @Bean public SerializerFactory serializerFactory() { // DO 自定义hessian反序列化 // step 1. 定义外部序列化工厂 ExtSerializerFactory extSerializerFactory = new ExtSerializerFactory(); extSerializerFactory.addSerializer(java.time.LocalDateTime.class,new LocalDateTimeSerializer()); extSerializerFactory.addDeserializer(java.time.LocalDateTime.class,new LocalDateTimeDeserializer()); // step 2. 序列化工厂 SerializerFactory serializerFactory = new SerializerFactory(); serializerFactory.addFactory(extSerializerFactory); return serializerFactory; } }

    因为是springmvc模式,所以在application-remote.xml中还需要指定序列化工厂,

    application-remote.xml:

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <bean id="demoService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl" value="${equaker-server-url}/demoService"/> <property name="serviceInterface" value="com.equaker.api.DemoService" /> <property name="serializerFactory" ref="serializerFactory"/> <property name="hessian2Request" value="true"/> </bean> </beans>

    这是控制层(调用端)需要配置的,主要介绍 serializerFactory与hessian2Request

    serializerFactory:指向hessianconfig中配置的序列化工厂,记得是ref.

    hessian2Request:配置了这个参数,序列化会使用Hessian2Out这个,而不是使用HessianOut,前一个支持二进制序列化,是最新支持的。

    server层

    同样需要配置序列化工厂,也就是上面的三个类。加入server即可。这里不同的是server端的application-remote.xml配置

    application-remote.xml:

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <bean name="/demoService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="serviceInterface" value="com.equaker.api.DemoService"/> <property name="service" ref="demoService"/> <property name="serializerFactory" ref="serializerFactory"/> <!-- <property name="isHessian2" value="true"/> --> </bean> </beans>

    注意:服务端主要是解析,所以isHessian2就不用配置了

    总结:在网上找了很久没有写好的代码,copy不到了,就自己慢慢扣源码,配置的这两个参数就是在源码里面看到的,hessian已经提供给我们了。但是文档太少,没人用。一般的自定义解析器都需要继承com.caucho.hessian.io.AbstractDeserializer/com.caucho.hessian.io.AbstractSerializer。记得重写里面的writeObject,writeMap等你需要的方法。

     


    最新回复(0)