不过在使用的过程中,我也发现了一些坑。一开始并没有意识到是Lombok的问题。后来跟踪其他对应组件的源码,发现是Lombok的问题!Setter-Getter方法坑问题发现我们在项目中主要使用了Lombok的Setter-Getter方法的注解,即组合注解@Data,但是在使用Mybatis插入数据的过程中,出现了问题。问题描述如下:我们有一个实体类:@DatapublicclassNMetaVerify{privateNMetaTypenMetaType;privateLongid;....otherattributes}我们在使用Mybatis插入数据的时候,发现其他属性可以正常插入,但是nMetaType属性是总是在数据库中为空。解决方案我调试项目代码调用Mybatis的插入SQL对应的方法时,看到NMetaVerify对象的nMetaType属性还有数据,但是插入执行后,数据库的nMetaType字段一直为null。一开始以为是我的枚举类型写错了。看了其他相同枚举类型的字段,也能正常插入数据库,更让我感到疑惑了。于是,我跟踪了Mybatis的源码,发现Mybatis在获取nMetaType属性时使用了反射,使用getxxxx方法获取。但是我发现nMetaType的get方法好像和Mybatis需要的getxxxx方法有点不一样。问题找到了!原因是Lombok对首字母小写,第二个字母大写的属性生成的get-set方法和Mybatis是一样的,思路或者Java官方认可的get-set方法不一样:get-#Lombok生成的set方法@DatapublicclassNMetaVerify{privateLongid;privateNMetaTypenMetaType;privateDatecreateTime;publicvoidlombokFound(){NMetaVerifynMetaVerify=newNMetaVerify();nMetaVerify.setNMetaType(NMetaType.TWO);//注意:nMetaType的set方法是setNMetaType,第n个字母是大写的,nMetaVerify.getNMetaType();//getxxxx方法也是大写的}}#idea,Mybatis,Java官方默认行为是:.id=id;}publicNMetaTypegetnMetaType(){//注意:nMetaType属性首字母小写returnnMetaType;}publicvoidsetnMetaType(NMetaTypenMetaType){//注意:finMetaType属性首字母小写this.nMetaType=nMetaType;}publicDategetCreateTime(){returncreateTime;}publicvoidsetCreateTime(DatecreateTime){this.createTime=createTime;}}Mybatis(3.4.6版本)解析get-set方法Get属性名源码:packageorg.apache.ibatis.reflection.property;importjava.util.Locale;importorg.apache.ibatis.reflection.ReflectionException;/***@authorClintonBegin*/publicfinalclassPropertyNamer{privatePropertyNamer(){//PreventInstantiationofStaticClass}publicstaticStringmethodToProperty(Stringname){if(name.startsWith("is")){//is开头一般是bool类型,直接从第二个(index)截取(简单粗暴)name=name.substring(2);}elseif(name.startsWith("get")||name.startsWith("set")){//set-get从第三个(index)截获name=name.substring(3);}else{thrownewReflectionException("Errorparsingpropertyname'"+name+"'.Didn'tstartwith'is','get'or'set'.");}//下面的判断很重要,可以分两句开始解释。解释如下//第一句:name.length()==1//属性只有一个字母,如privateintx;//对应的get-set方法为getX();setX(intx);//第二句:name.length()>1&&!Character.isUpperCase(name.charAt(1)))//属性name的长度大于1,第二句(charAt(1)in代码中,this1为数组下标)letterislowercase//如果第二个char为大写,则直接返回nameif(name.length()==1||(name.length()>1&&!Character.isUpperCase(姓名.charAt(1)))){name=name.substring(0,1).toLowerCase(Locale.ENGLISH)+name.substring(1);//让属性名首字母小写,然后加上以下内容}returnname;}publicstaticbooleanisProperty(Stringname){returnname.startsWith("get")||name.startsWith("set")||name.startsWith("is");}publicstaticbooleanisGetter(Stringname){returnname.startsWith("get")||name.startsWith("is");}publicstaticbooleanisSetter(Stringname){returnname.startsWith("set");}}Mybatis将get-set方法解析为属性名test:@TestpublicvoidfoundPropertyNamer(){StringisName="isName";StringgetName="getName";StringgetnMetaType="getnMetaType";StringgetNMetaType="getNMetaType";Stream.of(isName,getName,getnMetaType,getNMetaType).forEach(methodName->System.out.println("方法名是:"+methodName+"属性名:"+PropertyNamer.methodToProperty(methodName)));}#输出结果如下:方法名是:isName属性名:name方法名是:getName属性名:name我方法名称是:getnMetaType属性名称:nMetaType//thisand以下属性的第二个字母全部大写,所以直接返回名称的方法名称为:getNMetaType属性名:NMetaType解决方案解决方法如下:修改属性名,将第二个字母小写,或者指定所有属性的前缀这两个字母必须小写。如果数据库已经设计好,前后端接口已经对接,不想修改,那就用idea为这个特殊属性生成一个get-set方法。@Accessor(chain=true)注解的问题问题发现是在使用easyexcel(github.com/alibaba/eas...)导出的时候,发现之前的实体类导出是正常的,但是现在新添加的实体类不正常。经过对比,发现新添加的实体类添加了@Accessor(chain=true)注解。我们的目的主要是方便我们链式调用set方法:newUserDto().setUserName("").setAge(10).........setBirthday(newDate());原因是easyexcel底层使用了cglib作为反射工具包:com.alibaba.excel.read.listener.ModelBuildEventListenerclassBeanMap.create(resultModel).putAll(map)的第130行;最底层是cglib的BeanMap调用abstractpublicObjectput(Objectbean,Objectkey,Objectvalue)的方法;但是cglib使用Java的rt.jar中的Introspector类的方法:#Introspector.javaLine520if(int.class.equals(argTypes[0])&&name.startsWith(GET_PREFIX)){pd=newIndexedPropertyDescriptor(this.beanClass,name.substring(3),null,null,method,null);//下面这一行判断只获取返回值为void类型的setxxxx方法}elseif(void.class.equals(resultType)&&name.startsWith(SET_PREFIX)){//Simplesetterpd=newPropertyDescriptor(this.beanClass,name.substring(3),null,method);if(throwsException(method,PropertyVetoException.class)){pd.setConstrained(true);}}解决方案解决方案是如下:去掉Accessor注解。要么等easyexcel的作者把底层的cglib换掉要么别的,反正支持setxxx这个返回值不void的方法就好了。作者:liuxuzxx编辑:陶佳龙来源:https://juejin.im/post/6881432532332576781
