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 遠程服務。用戶端與服務端使用一套服務介面,而介面的具體實現是在服務端。
用戶端調用遠程方法時,會序列化要調用的(介面)方法和參數,發送到服務端。
服務端會根據請求還原序列化還原,調用具體方法擷取結果,並將結果序列化發給用戶端。
用戶端接收響應,將服務端結果還原序列化還原。
而如果用戶端與服務端代碼的版本不一致,就會產生該異常。