<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 5927字,需瀏覽 12分鐘

           ·

          2021-11-09 19:57

          1需求

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

          構(gòu)造Request類

          public?class?User?{
          ????private?String?name;
          ????private?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}
          }

          啪,我上來就一new

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

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

          2定位

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

          我是不會認(rèn)為這么簡單的代碼自己會出錯(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é)論:

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

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

          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)
          ?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í)不會異常失敗

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

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

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

          3解決

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

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

          public?class?User?{
          ????private?String?name;
          ????private?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?Integer?getAge()?{
          ????????return?age;
          ????}

          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????ObjectMapper?objectMapper?=?new?ObjectMapper();
          ????????String?request?=?objectMapper.writeValueAsString(new?User("niu",?18));
          ????????System.out.println(request);
          ????????//?輸出正常?:?{"name":"niu","age":18}
          ????}
          }

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

          public?class?User?{
          ????public?String?name;
          ????public?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}

          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????ObjectMapper?objectMapper?=?new?ObjectMapper();
          ????????String?request?=?objectMapper.writeValueAsString(new?User("niu",?18));
          ????????System.out.println(request);
          ????????//?輸出正常?:?{"name":"niu","age":18}
          ????}
          }

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

          3、注解 @JsonProperty

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

          public?class?User?{
          ????@JsonProperty("userName")
          ????private?String?name;
          ????@JsonProperty
          ????private?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}

          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????ObjectMapper?objectMapper?=?new?ObjectMapper();
          ????????String?request?=?objectMapper.writeValueAsString(new?User("niu",?18));
          ????????System.out.println(request);
          ????????//???{"userName":"niu","age":18}
          ????}
          }

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

          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)呢?

          public?class?User?{
          ????@JsonProperty("userName")
          ????private?String?name;
          ????@JsonProperty
          ????private?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????ObjectMapper?objectMapper?=?new?ObjectMapper();
          ????????String?request?=?objectMapper.writeValueAsString(new?User("niu",?18));
          ????????System.out.println(request);
          ????????//?{"age":18,"userName":"niu"}
          ????}
          }

          如果getter一個(gè)沒有的屬性,效果如何呢?

          public?class?User?{
          ????@JsonProperty("userName")
          ????private?String?name;
          ????@JsonProperty
          ????private?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.name?=?name;
          ????????this.age?=?age;
          ????}

          ????public?String?getName2()?{
          ????????return?name;
          ????}

          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????ObjectMapper?objectMapper?=?new?ObjectMapper();
          ????????String?request?=?objectMapper.writeValueAsString(new?User("niu",?18));
          ????????System.out.println(request);
          ????????//?{"age":18,"name2":"niu","userName":"niu"}
          ????}
          }

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

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

          4、特殊情況

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

          @Getter
          public?class?User?{
          ????@JsonProperty
          ????private?String?nAme;
          ????@JsonProperty
          ????private?Integer?age;

          ????public?User(String?name,?Integer?age)?{
          ????????this.nAme?=?name;
          ????????this.age?=?age;
          ????}

          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????ObjectMapper?objectMapper?=?new?ObjectMapper();
          ????????String?request?=?objectMapper.writeValueAsString(new?User("niu",?18));
          ????????System.out.println(request);
          ????????//?{"nAme":"niu","age":18,"name":"niu"}
          ????}
          }

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

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

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

          public?String?getNAme()?{
          ????return?this.nAme;
          }

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

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

          public?String?getnAme()?{
          ????return?nAme;
          }

          4小結(jié)

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

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

          —?【 THE END 】—
          本公眾號全部博文已整理成一個(gè)目錄,請?jiān)诠娞柪锘貜?fù)「m」獲取!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號并回復(fù) PDF?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 63
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲一级免费视频 | 三级片www. | 欧美成人激情视频 | 国产黄色在线免费观看 | 黄色电影一级片电影网址 |