寫在前面:
阿里代碼規範說自從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等你需要的方法。