发现一个好东西Protostuff, 据说可以不写proto文件来把对象序列化成二进制格式,用了一下果然好使。
public class Protostuff {
@SuppressWarnings("unchecked")
public static <T> byte[] serial(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
// RuntimeSchema会缓存类信息,不用自己实现缓存了,爽
Schema<T> schema = RuntimeSchema.getSchema(clazz);
// 缓存buff, 不够会自动增长
LinkedBuffer buffer = LinkedBuffer.allocate(1024);
// 序列化成protobuf的二进制数据
byte[] data = null;
try {
data = ProtobufIOUtil.toByteArray(obj, schema, buffer);
} finally {
buffer.clear();
}
return data;
}
public static <T> T deserial(byte[] data, Class<T> clazz) {
Schema<T> schema = RuntimeSchema.getSchema(clazz);
T message = schema.newMessage();
ProtobufIOUtil.mergeFrom(data, message, schema);
return message;
}
}
测试对象可以使用注解指定字段序号,排除字段等(另外还有Rpc,Request和Response等注解指定rpc服务):
public class Person{
@Tag(1)
public int id;
@Tag(2)
public String name;
@Exclude
public String email;
@Tag(3)
public int age;
@Tag(4)
public Student student;
@Tag(5)
public List<Student> list = new ArrayList<Student>();
@Tag(6)
public Map<String, Student> map = new HashMap<String, Student>();
@Tag(7)
public String s;
}
public class Student {
@Tag(1)
private String email;
@Tag(2)
private int age;
public Student(){
}
public Student(String email, int age) {
super();
this.email = email;
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试:
public static void main(String[] args)throws Exception {
Person person1 = new Person();
person1.id = 10086;
person1.name = "ken";
person1.email = "ken@iamcoding.com";
person1.age = 10;
person1.student = new Student("yuan@aliyun.com", 20);
person1.list.add(new Student("yuan2@aliyun.com", 30));
for(int i=0; i<2000; i++){
person1.list.add(new Student("yuan3@aliyun.com", 40));
}
person1.map.put("1", new Student("yuan4@aliyun.com", 400));
person1.map.put("2", new Student("yuan5@aliyun.com", 500));
// 序列化成protobuf的二进制数据
byte[] data = Protostuff.serial(person1);
Files.write(Paths.get("/data/Persion.data"), data);
// 反序列化
Person person2 = Protostuff.deserial(Files.readAllBytes(Paths.get("/data/Persion.data")), Person.class);
System.out.println(person2.id);
System.out.println(person2.name);
System.out.println(person2.email);
System.out.println(person2.age);
System.out.println(person2.student.getAge());
System.out.println(person2.student.getEmail());
System.out.println("***" + person2.list.size());
System.out.println("***" + person2.list.get(0).getAge());
System.out.println("***" + person2.list.get(0).getEmail());
System.out.println("===" + person2.map.get("1").getAge());
System.out.println("===" + person2.map.get("1").getEmail());
}
Protostuff使用还是比较简单的,但是没有发现序列化之前的回调方法注解和反序列化后的回调方法注解,不过这好实现不算问题。
另外对象增加字段后,仍然能够反序列化老数据,这个做的也不错。
Protostuff只能在JAVA中使用,完全可以替代FST,msgpack, 等一些二进制协议,用来将对象存储到缓存或者db中。
不过对于通信协议来说 写proto文件会更清晰, 所以通信使用protobuf更好!