自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一個(gè)Getter引發(fā)的血案

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

[[413398]]

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

1需求

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

構(gòu)造Request類

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

啪,我上來就一new

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

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

2定位

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

我是不會(huì)認(rèn)為這么簡單的代碼自己會(huì)出錯(cuò)的,不可能!!

經(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地方,于是本地測試驗(yàn)證了這一結(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這個(gè)配置表示如果某個(gè)bean序列化為空時(shí)不會(huì)異常失敗

  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); 

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

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

3解決

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

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

  1. public class User { 
  2.     private String name
  3.     private Integer age; 
  4.  
  5.     public User(String nameInteger 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 nameInteger 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 nameInteger 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. 

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

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

  1. public class User { 
  2.     @JsonProperty("userName"
  3.     private String name
  4.     @JsonProperty 
  5.     private Integer age; 
  6.  
  7.     public User(String nameInteger 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一個(gè)沒有的屬性,效果如何呢?

  1. public class User { 
  2.     @JsonProperty("userName"
  3.     private String name
  4.     @JsonProperty 
  5.     private Integer age; 
  6.  
  7.     public User(String nameInteger 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注解,先以注解為準(zhǔn)

然后利用反射找到對象類的所有g(shù)et方法,接下來去get,然后小寫化,作為json的每個(gè)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 nameInteger 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.     } 

這是因?yàn)閘ombok生成的getter會(huì)把屬性的第一個(gè)字母變成大寫,

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

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

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

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

此時(shí)之后序列化nAme

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

4小結(jié)

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

 

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

 

責(zé)任編輯:武曉燕 來源: 你呀不牛
相關(guān)推薦

2021-12-01 06:59:27

架構(gòu)

2018-11-22 15:50:27

MySQL數(shù)據(jù)庫雙引號(hào)

2021-01-25 08:08:22

APP機(jī)器人KOB

2021-02-01 10:42:47

MySQL雙引號(hào)數(shù)據(jù)庫

2017-05-22 08:35:07

MySQL雙引號(hào)錯(cuò)位

2017-08-25 16:38:05

表達(dá)式正則血案

2010-08-09 09:46:40

2017-03-20 19:40:29

AndroidSwipeRefres下拉刷新

2021-01-11 05:30:04

Boot 單機(jī)片

2012-02-13 09:42:41

備份服務(wù)器數(shù)據(jù)中心

2011-02-28 09:31:30

HashtableHashMap

2015-02-04 14:36:07

格式串漏洞Ghost漏洞安全漏洞

2011-11-25 13:04:43

空格usr

2019-09-09 08:30:57

MYSQL代碼數(shù)據(jù)庫

2023-01-11 08:41:47

微服務(wù)循環(huán)依賴

2013-12-05 10:50:13

2020-01-06 09:43:14

賠償TSB遷移

2017-06-12 16:13:13

曼聯(lián)皇馬傳真機(jī)

2016-12-01 09:30:03

運(yùn)維網(wǎng)絡(luò)網(wǎng)線

2010-11-04 09:11:34

Fedora 14評測
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)