最近在做一个dubbo接口的时候,突然想到如果将发送对象的serialVersionUID改成和接受对象的serialVersionUID不一致时,会不会导致反序列化失败?
都知道在java的序列化和反序列化里面,如果传输对象的serialVersionUID前后不一致,用java序列化和反序列化时会报错。
java.io.InvalidClassException:stream classdesc serialVersionUID = 6457272772, local class serialVersionUID = -1923645274767028479 (可以查询java中serialVersionUID 的作用)
但是,在实际的dubbo接口测试过程中,特意更改了传输对象的前后的serialVersionUID,结果是依然可以成功的接受对象,即成功的序列化和反序列化。
为什么?
开始困扰了好久,后来偶然看到一句话,“hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式。”。后来就想,是不是因为序列化方式有关,会不会是Hessian序列化时并不会在意serialVersionUID的变化。
为证实心中的疑虑。分别采用了Hessian和java两种序列化方式进行验证。
这里有2个工程,一个发送端,一个接收端,采用SpringBoot启动,两个工程里面都分别载入了实体对象Person。用于测试对象serialVersionUID相同和不相同的情况。
1.第一个工程,是传输对象接受端,这里反序列化。serializabletest-server。3个主类
1.1.Person.java 传输对象。
@ToString
@Data
public class Person implements Serializable {
private static final long serialVersionUID = -1923645274767028479L;
private String[] address;
private String name;
private int phone;
}
1.2. HessianController.java hessian反序列化接受类。通过一个Controller接受http提交过来的对象信息
@Slf4j
@RestController
@RequestMapping("/hessian")
public class HessianController {
@RequestMapping(value = "/hello")
public String helloWorld(HttpServletRequest request, HttpServletResponse response) throws IOException {
log.info("hessian反序列化开始------------------------");
ServletInputStream sis = request.getInputStream();
Hessian2Input h2i = new Hessian2Input(sis);
h2i.startMessage();
Person person = (Person) h2i.readObject();
h2i.completeMessage();
h2i.close();
sis.close();
log.info("hessian反序列化结果"+person.toString());
return person.toString();
}
}
1.3 JavaController.java Java反序列化接受类,通过一个Controller接受http提交过来的对象信息
@Slf4j
@RestController
@RequestMapping("/java")
public class JavaController {
@RequestMapping(value = "/seri")
public String helloWorld(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("java反序列化开始------------------------");
ServletInputStream sis = request.getInputStream();
ObjectInputStream is = new ObjectInputStream(sis);
Person person = (Person) is.readObject();
log.info("java"+person.toString());
return person.toString();
}
}
2、第二个工程,是传输对象发送端,这里进行对象序列化,并post发送请求。serializabletest-client。也是3个主类
2.1 Person.java
@ToString
@Data
public class Person implements Serializable {
//通过变更serialVersionUID,分别测试与serializabletest-server中相同和不同的情况
private static final long serialVersionUID = 6457272772L;
private String[] address;
private String name;
private int phone;
}
2.2 HessianTest.java Hessian序列化对象 并发送
public class HessianTest {
public static String urlName = "http://localhost:8080/hessian/hello";
public static void main(String[] args) throws Throwable {
// 序列化
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output h2o = new Hessian2Output(os);
h2o.startMessage();
h2o.writeObject(getPerson());
h2o.writeString("I am client.");
h2o.completeMessage();
h2o.close();
byte[] buffer = os.toByteArray();
os.close();
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(buffer,
ContentType.create("x-application/hessian", "UTF-8"));
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(urlName);
post.setEntity(byteArrayEntity);
CloseableHttpResponse response = client.execute(post);
System.out.println("response status:\n"
+ response.getStatusLine().getStatusCode());
HttpEntity body = response.getEntity();
System.out.println("body:"+body);
}
public static Person getPerson() {
Person person = new Person();
person.setAddress(new String[] { "Beijing", "TaiWan", "GuangZhou" });
person.setName("Jack");
person.setPhone(188888888);
return person;
}
2.3 JavaTest.java java序列化并发送
public class JavaTest {
public static String urlName = "http://localhost:8080/java/seri";
public static void main(String[] args) throws Throwable {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(getPerson());
byte[] buffer = bos.toByteArray();
os.close();
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(buffer,
ContentType.create("x-java-serialized-object", "UTF-8"));
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(urlName);
post.setEntity(byteArrayEntity);
CloseableHttpResponse response = client.execute(post);
System.out.println("response status:\n"
+ response.getStatusLine().getStatusCode());
HttpEntity body = response.getEntity();
System.out.println("body:"+body);
}
public static Person getPerson() {
Person person = new Person();
person.setAddress(new String[] { "Beijing", "TaiWan", "GuangZhou" });
person.setName("Jack");
person.setPhone(188888888);
return person;
}
}
3.测试:
这里分两种情况测试
3.1*测试第一种情况*
我们将发送端(serializabletest-client)中的person对象的serialVersionUID保持与serializabletest-server端一致,即将2.1中的serialVersionUID改为-1923645274767028479L 保持跟1.1一致。
分别运行HessianTest.java 和 JavaTest.java 得到serializabletest-server服务端中日志 如下
结论:当传输对象的serialVersionUID前后,这里两种方式Hessian和java都能成功序列化和反序列化操作
3.2*测试第二种情况*
将发送端和接受的端的serialVersionUID改为不一致。例,将2.1中的serialVersionUID改为6457272772L,1.1保持原来的值不变。 分别运行HessianTest.java 和 JavaTest.java 得到serializabletest-server服务端中日志 如下
结论:这里可以看到,在传输对象的serialVersionUID前后不一致时候,Hessian可以成功进行“反序列化”操作。但是java方式不能进行“反序列化”。