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

          記一次使用 Lombok 翻車造成的事故!

          共 5486字,需瀏覽 11分鐘

           ·

          2020-11-10 21:04

          作者:liuxuzxx
          https://juejin.im/post/6881432532332576781

          ?

          序言

          去年在項(xiàng)目當(dāng)中引入了Lombok插件,著實(shí)解放了雙手,代替了一些重復(fù)的簡(jiǎn)單工作(Getter,Setter,toString等方法的編寫)。

          但是,在使用的過(guò)程當(dāng)中,也發(fā)現(xiàn)了一些坑,開(kāi)始的時(shí)候并沒(méi)有察覺(jué)到是Lombok的問(wèn)題,后來(lái)跟蹤了對(duì)應(yīng)的其他組件的源碼,才發(fā)現(xiàn)是Lombok的問(wèn)題!

          ?

          Setter-Getter方法的坑

          問(wèn)題發(fā)現(xiàn)

          我們?cè)陧?xiàng)目當(dāng)中主要使用Lombok的Setter-Getter方法的注解,也就是組合注解@Data,但是在一次使用Mybatis插入數(shù)據(jù)的過(guò)程當(dāng)中,出現(xiàn)了一個(gè)問(wèn)題,問(wèn)題描述如下:

          我們有個(gè)實(shí)體類:

          @Data
          public?class?NMetaVerify{
          ????private?NMetaType?nMetaType;
          ????private?Long?id;
          ????....其他屬性
          }

          當(dāng)我們使用Mybatis插入數(shù)據(jù)的時(shí)候,發(fā)現(xiàn),其他屬性都能正常的插入,但是就是nMetaType屬性在數(shù)據(jù)庫(kù)一直是null.

          解決

          當(dāng)我debug項(xiàng)目代碼到調(diào)用Mybatis的插入SQL對(duì)應(yīng)的方法的時(shí)候,我看到NMetaVerify對(duì)象的nMetaType屬性還是有數(shù)據(jù)的,但是執(zhí)行插入之后,數(shù)據(jù)庫(kù)的nMetaType字段就是一直是null,原先我以為是我的枚舉類型寫法不正確,看了下別的同樣具有枚舉類型的字段,也是正常能插入到數(shù)據(jù)庫(kù)當(dāng)中的,這更讓我感覺(jué)到疑惑了.

          于是,我就跟蹤Mybatis的源碼,發(fā)現(xiàn)Mybatis在獲取這個(gè)nMetaType屬性的時(shí)候使用了反射,使用的是getxxxx方法來(lái)獲取的,但是我發(fā)現(xiàn)nMetaType的get方法好像有點(diǎn)和Mybatis需要的getxxxx方法長(zhǎng)的好像不一樣.問(wèn)題找到了!

          原因

          Lombok對(duì)于第一個(gè)字母小寫,第二個(gè)字母大寫的屬性生成的get-set方法和Mybatis以及idea或者說(shuō)是Java官方認(rèn)可的get-set方法生成的不一樣:

          Lombok生成的Get-Set方法

          @Data
          public?class?NMetaVerify?{
          ????private?Long?id;
          ????private?NMetaType?nMetaType;
          ????private?Date?createTime;
          ????
          ????public?void?lombokFound(){
          ????????NMetaVerify?nMetaVerify?=?new?NMetaVerify();
          ????????nMetaVerify.setNMetaType(NMetaType.TWO);?//注意:nMetaType的set方法為setNMetaType,第一個(gè)n字母大寫了,
          ????????nMetaVerify.getNMetaType();??????????????????????????????????//getxxxx方法也是大寫
          ????}
          }

          idea,Mybatis,Java官方默認(rèn)的行為為:

          public?class?NMetaVerify?{
          ????private?Long?id;
          ????private?NMetaType?nMetaType;
          ????private?Date?createTime;

          ????public?Long?getId()?{
          ????????return?id;
          ????}

          ????public?void?setId(Long?id)?{
          ????????this.id?=?id;
          ????}

          ????public?NMetaType?getnMetaType()?{//注意:nMetaType屬性的第一個(gè)字母小寫
          ????????return?nMetaType;
          ????}

          ????public?void?setnMetaType(NMetaType?nMetaType)?{//注意:nMetaType屬性的第一個(gè)字母小寫
          ????????this.nMetaType?=?nMetaType;
          ????}

          ????public?Date?getCreateTime()?{
          ????????return?createTime;
          ????}

          ????public?void?setCreateTime(Date?createTime)?{
          ????????this.createTime?=?createTime;
          ????}
          }

          Mybatis(3.4.6版本)解析get-set方法獲取屬性名字的源碼:

          package?org.apache.ibatis.reflection.property;

          import?java.util.Locale;

          import?org.apache.ibatis.reflection.ReflectionException;

          /**
          ?*?@author?Clinton?Begin
          ?*/

          public?final?class?PropertyNamer?{

          ?????private?PropertyNamer()?{
          ?????????//?Prevent?Instantiation?of?Static?Class
          ???????}

          ????public?static?String?methodToProperty(String?name)?{
          ??????if?(name.startsWith("is"))?{//is開(kāi)頭的一般是bool類型,直接從第二個(gè)(索引)開(kāi)始截取(簡(jiǎn)單粗暴)
          ??????????name?=?name.substring(2);
          ??????}?else?if?(name.startsWith("get")?||?name.startsWith("set"))?{//set-get的就從第三個(gè)(索引)開(kāi)始截取
          ??????????name?=?name.substring(3);
          ??????}?else?{
          ??????????throw?new?ReflectionException("Error?parsing?property?name?'"?+?name?+?"'.??Didn't?start?with?'is',?'get'?or?'set'.");
          ??????}
          ???????????//下面這個(gè)判斷很重要,可以分成兩句話開(kāi)始解釋,解釋如下
          ????????????//第一句話:name.length()==1
          ????????????//???????對(duì)于屬性只有一個(gè)字母的,例如private?int?x;
          ????????????//??????????對(duì)應(yīng)的get-set方法是getX();setX(int?x);
          ????????????//第二句話:name.length()?> 1 && !Character.isUpperCase(name.charAt(1)))
          ????????????//??????屬性名字長(zhǎng)度大于1,并且第二個(gè)(代碼中的charAt(1),這個(gè)1是數(shù)組下標(biāo))字母是小寫的
          ????????????//??????如果第二個(gè)char是大寫的,那就直接返回name
          ??????if?(name.length()?==?1?||?(name.length()?>?1?&&?!Character.isUpperCase(name.charAt(1))))?{
          ??????????name?=?name.substring(0,?1).toLowerCase(Locale.ENGLISH)?+?name.substring(1);//讓屬性名第一個(gè)字母小寫,然后加上后面的內(nèi)容
          ??????}

          ??????return?name;
          ????}

          ????public?static?boolean?isProperty(String?name)?{
          ???????return?name.startsWith("get")?||?name.startsWith("set")?||?name.startsWith("is");
          ????}

          ????public?static?boolean?isGetter(String?name)?{
          ???????return?name.startsWith("get")?||?name.startsWith("is");
          ????}

          ????public?static?boolean?isSetter(String?name)?{
          ???????return?name.startsWith("set");
          ????}

          }

          Mybatis解析get-set方法為屬性名字測(cè)試

          ????@Test
          ????public?void?foundPropertyNamer()?{
          ????????String?isName?=?"isName";
          ????????String?getName?=?"getName";
          ????????String?getnMetaType?=?"getnMetaType";
          ????????String?getNMetaType?=?"getNMetaType";

          ????????Stream.of(isName,getName,getnMetaType,getNMetaType)
          ????????????????.forEach(methodName->System.out.println("方法名字是:"+methodName+"?屬性名字:"+?PropertyNamer.methodToProperty(methodName)));
          ????}

          輸出結(jié)果如下:

          ????方法名字是:isName?屬性名字:name?
          ????方法名字是:getName?屬性名字:name?
          ????方法名字是:getnMetaType?屬性名字:nMetaType?//這個(gè)以及下面的屬性第二個(gè)字母都是大寫,所以直接返回name
          ????方法名字是:getNMetaType?屬性名字:NMetaType

          解決方案

          1. 修改屬性名字,讓第二個(gè)字母小寫,或者說(shuō)是規(guī)定所有的屬性的前兩個(gè)字母必須小寫

          2. 如果數(shù)據(jù)庫(kù)已經(jīng)設(shè)計(jì)好,并且前后端接口對(duì)接好了,不想修改,那就專門為這種特殊的屬性使用idea生成get-set方法復(fù)制代碼

          ?

          @Accessor(chain = true)注解的問(wèn)題

          問(wèn)題發(fā)現(xiàn)

          在使用 easyexcel 導(dǎo)出的時(shí)候,發(fā)現(xiàn)以前的實(shí)體類導(dǎo)出都很正常,但是現(xiàn)在新加的實(shí)體類不正常了,比對(duì)了發(fā)現(xiàn),新加的實(shí)體類增加了@Accessor(chain = true)注解,我們的目的主要是方便我們鏈?zhǔn)秸{(diào)用set方法:

          new?UserDto()
          .setUserName("")
          .setAge(10)
          ........
          .setBirthday(new?Date());

          原因

          easyexcel底層使用的是cglib來(lái)做反射工具包的:

          com.alibaba.excel.read.listener.ModelBuildEventListener?類的第130行
          BeanMap.create(resultModel).putAll(map);

          最底層的是cglib的BeanMap的這個(gè)方法調(diào)用

          abstract?public?Object?put(Object?bean,?Object?key,?Object?value);

          但是cglib使用的是Java的rt.jar里面的一個(gè)Introspector這個(gè)類的方法:

          Introspector.java 第520行

          if?(int.class.equals(argTypes[0])?&&?name.startsWith(GET_PREFIX))?{
          ???pd?=?new?IndexedPropertyDescriptor(this.beanClass,?name.substring(3),?null,?null,?method,?null);
          ???//下面這行判斷,只獲取返回值是void類型的setxxxx方法
          ?}?else?if?(void.class.equals(resultType)?&&?name.startsWith(SET_PREFIX))?{
          ????//?Simple?setter
          ????pd?=?new?PropertyDescriptor(this.beanClass,?name.substring(3),?null,?method);
          ????if?(throwsException(method,?PropertyVetoException.class))?{
          ???????pd.setConstrained(true);
          ????}

          }

          解決方案

          1. 去掉Accessor注解

          2. 要么就等待easyexcel的作者替換掉底層的cglib或者是其他,反正是支持獲取返回值不是void的setxxx方法就行復(fù)制代碼


          有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  免费看无码一级A片在线播放 | 午夜成人性 | 91超碰大香蕉 | 无码一区二区波多野结衣播放搜索 | 亚洲涩网 |