lombok1.16.20序列化bug分析

Fs發表於2019-04-14

摘要

記錄下使用lombok遇到的反序列問題,一開始在lombok1.16.18中並沒有發現,然後應用中沒有指定lombok全域性版本,引入的其他二方包將lombok版本提升到了1.16.20,然後報錯。因為這個問題需要允許時才能發現,很可能會造成線上故障,所以不能等到出現問題時才發現,需要提前知曉。

錯誤棧

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.fs.jackson.Address` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"id":1,"address":"address"}"; line: 1, column: 2]
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451)
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
	at Application.testSeriable(Application.java:36)
	at Application.run(Application.java:24)
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:790)
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
	at Application.main(Application.java:27)
複製程式碼

問題

問題1:

手賤把一個private修飾符寫成了final

@Data
public class AddressVO {
     final Long id;
    private String address;
}
複製程式碼

問題2 使用了Bulider類

@Data
@Accessors(chain = true)
@Builder
public class Address {
    private Long id;
    private String address;
}
複製程式碼

分析

分析lombok導致的問題很簡單,檢視下編譯後的class檔案即可。 使用jd-gui反編譯工具檢視一下

1.6.18

public class AddressByBuilder {
    private Long id;
    private String address;

    public static class AddressBuilder {
        private Long id;
        private String address;
        // 省略
    }

    @ConstructorProperties({ "id", "address" })
    AddressByBuilder(Long id, String address) {
        this.id = id;
        this.address = address;
    }
}
複製程式碼

1.16.20

public class AddressByLombok20 {

    private Long id;
    private String address;
	// 建構函式
    AddressByLombok20(Long id, String address) {
        this.id = id;
        this.address = address;
    }
}

複製程式碼

對比1.16.28與1.16.20發現建構函式發生了變化。1.16.20中建構函式少了@ConstructorProperties({ "id", "address" }) JDK1.6中出來的

The annotation shows that the first parameter of the constructor can be retrieved with the getX() method and the second with the getY() method. Since parameter names are not in general available at runtime, without the annotation there would be no way to know whether the parameters correspond to getX() and getY() or the other way around.

@ConstructorProperties()註解用於建構函式上,表示建構函式可以通過GetName來找到,第一個引數可以使用getX()方法檢索,第二個引數可以使用方法檢索getY()。由於方法引數名一般在執行時不可見,如果沒有標註就沒有辦法知道引數是否符合getX() 和getY()或周圍的其他方法。

這顯然是lombok升級過程中的一個不相容的改造。

因為我們都沒有定義無參建構函式,所以會找已有的建構函式,然後匹配getter/setter函式。

解決

1.maven中指定lombok固定版本,使用1.16.18版本,程式碼層面不需要做更改 2.類中新增無參構造器

建議

需要序列化的類,比如與前端互動,rpc呼叫,都加上無參構造器,相容性比較好
複製程式碼

參考

tutorials.jenkov.com/java-json/j… www.jianshu.com/p/f826ba2db…

相關文章