java.io.InvalidClassException: <className>; incompatible types for field <fieldName> 異常追蹤
異常信息
後臺日誌中產生了如下異常信息:
java.io.InvalidClassException: <className>; incompatible types for field <fieldName>
at java.io.ObjectStreamClass.matchFields(ObjectStreamClass.java:2299) ~[?:1.8.0_74]
at java.io.ObjectStreamClass.getReflector(ObjectStreamClass.java:2193) ~[?:1.8.0_74]
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:669) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1707) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1345) ~[?:1.8.0_74]
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2000) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1924) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351) ~[?:1.8.0_74]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371) ~[?:1.8.0_74]
涉及到OutputInputStream.readObject,因此猜測是對象反序列化時,而產生的問題。
異常來源
private static ObjectStreamField[] matchFields(ObjectStreamField[] fields,
ObjectStreamClass localDesc)
throws InvalidClassException{
// ...
for (int j = 0; j < localFields.length; j++) {
ObjectStreamField lf = localFields[j];
if (f.getName().equals(lf.getName())) {
if ((f.isPrimitive() || lf.isPrimitive()) &&
f.getTypeCode() != lf.getTypeCode())
{
throw new InvalidClassException(localDesc.name,
"incompatible types for field " + f.getName());
}
if (lf.getField() != null) {
m = new ObjectStreamField(
lf.getField(), lf.isUnshared(), false);
} else {
m = new ObjectStreamField(
lf.getName(), lf.getSignature(), lf.isUnshared());
}
}
}
// ...
}
異常重現
import java.io.Serializable;
public class Request implements Serializable {
private static final long serialVersionUID = 3663364724485038109L;
public Integer status;
public Integer addressStatus;
// setter getter
}
序列化Request對象
public class Serialization {
public static void main(String[] args) throws IOException, ClassNotFoundException{
Request r1 = new Request();
r1.setAddressStatus(100);
r1.setStatus(200);
Request r2 = new Request();
r2.setAddressStatus(300);
r2.setStatus(400);
File file = new File("D:/MySQL/workspace/spring/resources/request.ser");
serializable(file, r1, r2);
Request[] rs = deserializable(file);
for (Request request : rs)
System.out.println(request.getAddressStatus() + " " + request.getStatus());
}
public static void serializable(File file, Request... requests) throws IOException {
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
if (requests != null)
stream.writeObject(requests);
stream.close();
}
public static Request[] deserializable(File file) throws IOException, ClassNotFoundException {
ObjectInputStream stream = new ObjectInputStream(new FileInputStream(file));
Request[] requests = (Request[])stream.readObject();
stream.close();
return requests;
}
}
修改Request作用域類型
import java.io.Serializable;
public class Request implements Serializable {
private static final long serialVersionUID = 3663364724485038109L;
public int status;
public int addressStatus;
// setter getter
}
反序列化對象
public class Serialization {
public static void main(String[] args) throws IOException, ClassNotFoundException{
Request r1 = new Request();
r1.setAddressStatus(100);
r1.setStatus(200);
Request r2 = new Request();
r2.setAddressStatus(300);
r2.setStatus(400);
File file = new File("D:/MySQL/workspace/spring/resources/request.ser");
Request[] rs = deserializable(file);
for (Request request : rs)
System.out.println(request.getAddressStatus() + " " + request.getStatus());
}
public static void serializable(File file, Request... requests) throws IOException {
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
if (requests != null)
stream.writeObject(requests);
stream.close();
}
public static Request[] deserializable(File file) throws IOException, ClassNotFoundException {
ObjectInputStream stream = new ObjectInputStream(new FileInputStream(file));
Request[] requests = (Request[])stream.readObject();
stream.close();
return requests;
}
}
將會拋出如下異常
Exception in thread "main" java.io.InvalidClassException: com.spring.domain.Request; incompatible types for field addressStatus
at java.io.ObjectStreamClass.matchFields(ObjectStreamClass.java:2399)
at java.io.ObjectStreamClass.getReflector(ObjectStreamClass.java:2293)
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:741)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1966)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1561)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at com.spring.domain.Serialization.deserializable(Serialization.java:38)
at com.spring.domain.Serialization.main(Serialization.java:24)
調試分析,可知反序列化類型爲int和對象的序列化類型爲Integer, 因此產生異常。
總結
項目中使用了Spring httpinvoker 遠程服務。客戶端與服務端使用一套服務接口,而接口的具體實現是在服務端。
客戶端調用遠程方法時,會序列化要調用的(接口)方法和參數,發送到服務端。
服務端會根據請求反序列化還原,調用具體方法獲取結果,並將結果序列化發給客戶端。
客戶端接收響應,將服務端結果反序列化還原。
而如果客戶端與服務端代碼的版本不一致,就會產生該異常。