日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

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

新聞中心

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

本文轉(zhuǎn)載自微信公眾號(hào)「你呀不?!梗髡卟慌?。轉(zhuǎn)載本文請(qǐng)聯(lián)系你呀不牛公眾號(hào)。

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

1需求

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

構(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.     } 

啪,我上來(lái)就一new

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

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

2定位

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

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

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

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

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

能出現(xiàn)問(wèn)題的地方只能是序列化JSON地方,于是本地測(cè)試驗(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); 

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

 
 
 
 
  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) 

通過(guò)查詢異常資料,解決掉這種異常需要在增加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

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

3解決

再來(lái)分析一下原因,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 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、或者把屬性訪問(wèn)權(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.     } 

來(lái)看下注解@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 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一個(gè)沒(méi)有的屬性,效果如何呢?

 
 
 
 
  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.     } 

這說(shuō)明如果有@JsonProperty注解,先以注解為準(zhǔn)

然后利用反射找到對(duì)象類的所有g(shù)et方法,接下來(lái)去get,然后小寫化,作為json的每個(gè)key值,而get方法的返回值作為value。接下來(lái)再反射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.     } 

這是因?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都是在自以為沒(méi)有問(wèn)題的地方產(chǎn)生,看似簡(jiǎn)單,更需要小心,同時(shí)也需要多注意序列化原理,整體感覺(jué)序列化還是用Gson更省心,完全不用關(guān)心Getter和Setter方法,會(huì)完全按照屬性名來(lái)序列化。

本文的涉及的bug過(guò)程和解決方式希望對(duì)你也有所幫助,再見(jiàn)。


網(wǎng)站欄目:一個(gè)Getter引發(fā)的血案
標(biāo)題URL:http://www.5511xx.com/article/dpgpigh.html