springmvc+hessian 的LocaldateTime 序列化问题

写在前面:

     阿里代码规范说自从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(initValue%1000))*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等你需要的方法。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值