日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
一個Getter引發(fā)的血案

本文轉(zhuǎn)載自微信公眾號「你呀不?!?,作者不牛。轉(zhuǎn)載本文請聯(lián)系你呀不牛公眾號。

創(chuàng)新互聯(lián)"三網(wǎng)合一"的企業(yè)建站思路。企業(yè)可建設(shè)擁有電腦版、微信版、手機版的企業(yè)網(wǎng)站。實現(xiàn)跨屏營銷,產(chǎn)品發(fā)布一步更新,電腦網(wǎng)絡(luò)+移動網(wǎng)絡(luò)一網(wǎng)打盡,滿足企業(yè)的營銷需求!創(chuàng)新互聯(lián)具備承接各種類型的成都網(wǎng)站設(shè)計、成都網(wǎng)站制作項目的能力。經(jīng)過十多年的努力的開拓,為不同行業(yè)的企事業(yè)單位提供了優(yōu)質(zhì)的服務(wù),并獲得了客戶的一致好評。

1需求

最近做一了個需求,調(diào)用其他服務(wù)的REST接口,感覺很簡單,于是迅速就搞起來了

構(gòu)造Request類

 
 
 
 
  1. public class User { 
  2.     private String name; 
  3.     private Integer age; 
  4.  
  5.     public User(String name, Integer age) { 
  6.         this.name = name; 
  7.         this.age = age; 
  8.     } 

啪,我上來就一new

 
 
 
 
  1. service.sendRequest(new User("niu", 18)); 

打完,收工,又是努力工作(摸魚)的一天。

2定位

但是,某天晚上8點,測試人員突然給我打電話,說調(diào)用失敗,同時本身又缺少打印,沒有辦法具體哪出問題了。

我是不會認為這么簡單的代碼自己會出錯的,不可能!!

經(jīng)過網(wǎng)絡(luò)抓包后發(fā)現(xiàn),收到的參數(shù)都是null,但是我這邊明明調(diào)用構(gòu)造器傳入?yún)?shù)了

難道出現(xiàn)靈異事件了?

經(jīng)過分析,整體數(shù)據(jù)流為:

能出現(xiàn)問題的地方只能是序列化JSON地方,于是本地測試驗證了這一結(jié)論:

 
 
 
 
  1. public static void main(String[] args) throws IOException { 
  2.     ObjectMapper objectMapper = new ObjectMapper(); 
  3.     String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  4.     System.out.println(request); 

雖然是出問題了,但是序列化并沒有轉(zhuǎn)為屬性為null的對象,而是直接拋出異常

 
 
 
 
  1. Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class online.jvm.bean.User and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) 
  2.  at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) 

通過查詢異常資料,解決掉這種異常需要在增加Jackson的序列化配置FAIL_ON_EMPTY_BEANS,F(xiàn)AIL_ON_EMPTY_BEANS這個配置表示如果某個bean序列化為空時不會異常失敗

 
 
 
 
  1. public static void main(String[] args) throws IOException { 
  2.     ObjectMapper objectMapper = new ObjectMapper(); 
  3.     objectMapper.configure(FAIL_ON_EMPTY_BEANS, false); 
  4.     String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  5.     System.out.println(request); 

這種就不會報錯,而是返回序列化成空串,也就導(dǎo)致接受方為屬性都為null

通過看自研RPC框架看到是有該FAIL_ON_EMPTY_BEANS的配置

3解決

再來分析一下原因,Jackson序列化時需要調(diào)用bean的getter方法

1、寫上getter后再看下結(jié)果:

 
 
 
 
  1. public class User { 
  2.     private String name; 
  3.     private Integer age; 
  4.  
  5.     public User(String name, Integer age) { 
  6.         this.name = name; 
  7.         this.age = age; 
  8.     } 
  9.  
  10.     public String getName() { 
  11.         return name; 
  12.     } 
  13.  
  14.     public Integer getAge() { 
  15.         return age; 
  16.     } 
  17.  
  18.     public static void main(String[] args) throws IOException { 
  19.         ObjectMapper objectMapper = new ObjectMapper(); 
  20.         String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  21.         System.out.println(request); 
  22.         // 輸出正常 : {"name":"niu","age":18} 
  23.     } 

2、或者把屬性訪問權(quán)限改為public

 
 
 
 
  1. public class User { 
  2.     public String name; 
  3.     public Integer age; 
  4.  
  5.     public User(String name, Integer age) { 
  6.         this.name = name; 
  7.         this.age = age; 
  8.     } 
  9.  
  10.     public static void main(String[] args) throws IOException { 
  11.         ObjectMapper objectMapper = new ObjectMapper(); 
  12.         String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  13.         System.out.println(request); 
  14.         // 輸出正常 : {"name":"niu","age":18} 
  15.     } 

但是如果要求不能暴露bean的屬性即使是getter也不行呢?

3、注解 @JsonProperty

這是就需要使用Jackson提供的注解 @JsonProperty

 
 
 
 
  1. public class User { 
  2.     @JsonProperty("userName") 
  3.     private String name; 
  4.     @JsonProperty 
  5.     private Integer age; 
  6.  
  7.     public User(String name, Integer age) { 
  8.         this.name = name; 
  9.         this.age = age; 
  10.     } 
  11.  
  12.     public static void main(String[] args) throws IOException { 
  13.         ObjectMapper objectMapper = new ObjectMapper(); 
  14.         String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  15.         System.out.println(request); 
  16.         //   {"userName":"niu","age":18} 
  17.     } 

來看下注解@JsonProperty的源碼注釋

 
 
 
 
  1. Marker annotation that can be used to define a non-static method as a "setter" or "getter" for a logical property (depending on its signature), or non-static object field to be used (serialized, deserialized) as a logical property. 

大體意思是注解如果用在屬性上相當于為該屬性定義getter和setter。

那如果既有g(shù)etter又有@JsonProperty注解,以哪個為準呢?

 
 
 
 
  1. public class User { 
  2.     @JsonProperty("userName") 
  3.     private String name; 
  4.     @JsonProperty 
  5.     private Integer age; 
  6.  
  7.     public User(String name, Integer age) { 
  8.         this.name = name; 
  9.         this.age = age; 
  10.     } 
  11.  
  12.     public String getName() { 
  13.         return name; 
  14.     } 
  15.  
  16.     public static void main(String[] args) throws IOException { 
  17.         ObjectMapper objectMapper = new ObjectMapper(); 
  18.         String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  19.         System.out.println(request); 
  20.         // {"age":18,"userName":"niu"} 
  21.     } 

如果getter一個沒有的屬性,效果如何呢?

 
 
 
 
  1. public class User { 
  2.     @JsonProperty("userName") 
  3.     private String name; 
  4.     @JsonProperty 
  5.     private Integer age; 
  6.  
  7.     public User(String name, Integer age) { 
  8.         this.name = name; 
  9.         this.age = age; 
  10.     } 
  11.  
  12.     public String getName2() { 
  13.         return name; 
  14.     } 
  15.  
  16.     public static void main(String[] args) throws IOException { 
  17.         ObjectMapper objectMapper = new ObjectMapper(); 
  18.         String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  19.         System.out.println(request); 
  20.         // {"age":18,"name2":"niu","userName":"niu"} 
  21.     } 

這說明如果有@JsonProperty注解,先以注解為準

然后利用反射找到對象類的所有g(shù)et方法,接下來去get,然后小寫化,作為json的每個key值,而get方法的返回值作為value。接下來再反射field,添加到j(luò)son中。

4、特殊情況

還有一種比較特殊的情況, getter方法由lombok生成,且屬性的次首字母是大寫:

 
 
 
 
  1. @Getter 
  2. public class User { 
  3.     @JsonProperty 
  4.     private String nAme; 
  5.     @JsonProperty 
  6.     private Integer age; 
  7.  
  8.     public User(String name, Integer age) { 
  9.         this.nAme = name; 
  10.         this.age = age; 
  11.     } 
  12.  
  13.     public static void main(String[] args) throws IOException { 
  14.         ObjectMapper objectMapper = new ObjectMapper(); 
  15.         String request = objectMapper.writeValueAsString(new User("niu", 18)); 
  16.         System.out.println(request); 
  17.         // {"nAme":"niu","age":18,"name":"niu"} 
  18.     } 

這是因為lombok生成的getter會把屬性的第一個字母變成大寫,

序列化時會把get后與小寫字母中間的大寫變成小寫,也就是會把NA變成小寫

所以序列化結(jié)果會有name(getter獲取)和nAme(注解獲取)兩個屬性

 
 
 
 
  1. public String getNAme() { 
  2.     return this.nAme; 

如果我們自己用idea快捷鍵生成getter,

此時之后序列化nAme

 
 
 
 
  1. public String getnAme() { 
  2.     return nAme; 

4小結(jié)

許多bug都是在自以為沒有問題的地方產(chǎn)生,看似簡單,更需要小心,同時也需要多注意序列化原理,整體感覺序列化還是用Gson更省心,完全不用關(guān)心Getter和Setter方法,會完全按照屬性名來序列化。

本文的涉及的bug過程和解決方式希望對你也有所幫助,再見。


當前標題:一個Getter引發(fā)的血案
URL鏈接:http://www.dlmjj.cn/article/dpgpigh.html