坑爹的 Lombok,把我害惨了!

来源:juejin.im/post/6881432532332576781

序言

去年在项目当中引入了Lombok插件,着实解放了双手,代替了一些重复的简单工作(Getter,Setter,toString等方法的编写),但是,在使用的过程当中,也发现了一些坑,开始的时候并没有察觉到是Lombok的问题,后来跟踪了对应的其他组件的源码,才发现是Lombok的问题!

Setter-Getter方法的坑

问题发现

我们在项目当中主要使用Lombok的Setter-Getter方法的注解,也就是组合注解@Data,但是在一次使用Mybatis插入数据的过程当中,出现了一个问题,问题描述如下:

我们有个实体类:
@Data
public class NMetaVerify{private NMetaType nMetaType;private Long id;....其他属性
}

当我们使用Mybatis插入数据的时候,发现,其他属性都能正常的插入,但是就是nMetaType属性在数据库一直是null

解决

当我debug项目代码到调用Mybatis的插入SQL对应的方法的时候,我看到NMetaVerify对象的nMetaType属性还是有数据的,但是执行插入之后,数据库的nMetaType字段就是一直是null,原先我以为是我的枚举类型写法不正确,看了下别的同样具有枚举类型的字段,也是正常能插入到数据库当中的,这更让我感觉到疑惑了.于是,我就跟踪Mybatis的源码,发现Mybatis在获取这个nMetaType属性的时候使用了反射,使用的是getxxxx方法来获取的,但是我发现nMetaType的get方法好像有点和Mybatis需要的getxxxx方法长的好像不一样.问题找到了!

原因

Lombok对于第一个字母小写,第二个字母大写的属性生成的get-set方法和Mybatis以及idea或者说是Java官方认可的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,第一个n字母大写了,nMetaVerify.getNMetaType();                                  //getxxxx方法也是大写}
}#idea,Mybatis,Java官方默认的行为为:
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属性的第一个字母小写return nMetaType;}public void setnMetaType(NMetaType nMetaType) {//注意:nMetaType属性的第一个字母小写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开头的一般是bool类型,直接从第二个(索引)开始截取(简单粗暴)name = name.substring(2);} else if (name.startsWith("get") || name.startsWith("set")) {//set-get的就从第三个(索引)开始截取name = name.substring(3);} else {throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");}//下面这个判断很重要,可以分成两句话开始解释,解释如下//第一句话:name.length()==1//       对于属性只有一个字母的,例如private int x;//          对应的get-set方法是getX();setX(int x);//第二句话:name.length() > 1 && !Character.isUpperCase(name.charAt(1)))//      属性名字长度大于1,并且第二个(代码中的charAt(1),这个1是数组下标)字母是小写的//      如果第二个char是大写的,那就直接返回nameif (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);//让属性名第一个字母小写,然后加上后面的内容}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方法为属性名字测试

@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)));
}#输出结果如下:
方法名字是:isName 属性名字:name 
方法名字是:getName 属性名字:name 
方法名字是:getnMetaType 属性名字:nMetaType //这个以及下面的属性第二个字母都是大写,所以直接返回name
方法名字是:getNMetaType 属性名字:NMetaType

解决方案

1.修改属性名字,让第二个字母小写,或者说是规定所有的属性的前两个字母必须小写
2.如果数据库已经设计好,并且前后端接口对接好了,不想修改,那就专门为这种特殊的属性使用idea生成get-set方法

@Accessor(chain = true)注解的问题

问题发现

在使用easyexcel(https://github.com/alibaba/easyexcel) 导出的时候,发现以前的实体类导出都很正常,但是现在新加的实体类不正常了,比对了发现,新加的实体类增加了@Accessor(chain = true)注解,我们的目的主要是方便我们链式调用set方法:

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

原因

easyexcel底层使用的是cglib来做反射工具包的:

com.alibaba.excel.read.listener.ModelBuildEventListener 类的第130行
BeanMap.create(resultModel).putAll(map);最底层的是cglib的BeanMap的这个方法调用abstract public Object put(Object bean, Object key, Object value);

但是cglib使用的是Java的rt.jar里面的一个Introspector这个类的方法:

# 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 setterpd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);if (throwsException(method, PropertyVetoException.class)) {pd.setConstrained(true);}
}

解决方案

1.去掉Accessor注解
2.要么就等待easyexcel的作者替换掉底层的cglib或者是其他,反正是支持获取返回值不是void的setxxx方法就行


往期推荐

Java中不可或缺的59个小技巧,贼好用!


2万字长文包教包会 JVM 内存结构


2万字,看完这篇才敢说自己真的懂线程池!


关注我,每天陪你进步一点点!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545214.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构学习笔记(六)链表算法题

假期结束,看点题目。 第一题 问题 设顺序表用数组A[]表示,表中元素存储在数组下标1~mn的范围内,前m个元素递增有序,后n个元素递增有序,设计一个算法,使得整个顺序表有序。 (1)给出算…

安卓第一次搭建C/S架构

1_数据库 2_服务端 服务端简单搭建准入门 使用json,导入jar包复制这段内容后打开百度网盘手机App,操作更方便哦 提取码: 3afj 在项目中建一个文件夹并粘贴进去 json与list的互转: import com.alibaba.fastjson.JSON;import j…

vb.net 中最小化到托盘和锁定窗体大小的问题(notifyIcon的两个重要属性)

最小化到托盘需要用到 NotifyIcon 控件,从工具箱中找到并添加,其余代码如下: 最小化到右下角 & 锁定窗口大小Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.ResizeIf Me.WindowState …

面试官不讲武德,竟然问了我18个JVM问题!

前言GC 对于Java 来说重要性不言而喻,不论是平日里对 JVM 的调优还是面试中的无情轰炸。这篇文章我会以一问一答的方式来展开有关 GC 的内容。本文章所说的 GC 实现没有特殊说明的话,默认指的是 HotSpot 的。我先将十八个问题都列出来,大家可…

2月第3周国内域名商TOP10:爱名网排名升至第八

IDC评述网(idcps.com)02月26日报道:根据WebHosting.info公布的最新数据显示,在2月第3周,国内域名商域名总量十强总体呈下降趋势。其中,降幅最大的是DNSPod,净减16,762个。另外,中国数…

Android JSON数据与实体类之间的相互转化-------GSON的简单用法

Android JSON数据与实体类之间的相互转化-------GSON的用法1_Gson的导入1.1_方法一:直接导入jar包1.2_方法二:引入依赖2_json形式的字符串互转实体对象2.1_json字符串与单个实体对象互转2.2_json与list互转3_遇到的问题3.1_前后端对象成员变量类型不一致…

Java StreamTokenizer quoteChar()方法与示例

StreamTokenizer类quoteChar()方法 (StreamTokenizer Class quoteChar() method) quoteChar() method is available in java.io package. quoteChar()方法在java.io包中可用。 quoteChar() method denotes that matching pairs of this character delimiter, string constants …

decode 实例

以往相关材料: http://blog.csdn.net/arrowzz/article/details/17144651 http://blog.csdn.net/arrowzz/article/details/17144669表id,name,score1,小明,胜2,小明,胜3,小李,负4,小李,负5,小明,负6,小李,胜7,小李,胜效果name,胜,负小明,2,1小李,2,2创建表&#xf…

5种SpringBoot热部署方式,你用哪种?

来源 | my.oschina.net/ruoli/blog/1590148Spring Boot 中 5 种热部署方式如下:1、模板热部署2、使用调试模式Debug实现热部署3、spring-boot-devtools4、Spring Loaded5、JRebel接下来我们分别来看。1、模板热部署在 Spring Boot 中,模板引擎的页面默认…

IBM 前面板显示信息提示

ps1 指示灯:当此指示灯发亮时,表明电源1 出现故障。 ps2 指示灯:当此指示灯发亮时,表明电源2 出现故障。 temp 指示灯:当此指示灯发亮时,表明系统温度超出阈值级别。 风扇指示灯:当此指…

ContextMenu长按事件

/* ContextMenu菜单就是长按某一个组件,就会在屏幕的中间弹出ContextMenu,这里设置为长按文本框弹出ContextMenu菜单*/public class MyContextMenu extends AppCompatActivity {/** Called when the activity is first created. */final static int CONT…

observable_Java Observable deleteObserver()方法与示例

observable可观察的类deleteObserver()方法 (Observable Class deleteObserver() method) deleteObserver() method is available in java.util package. deleteObserver()方法在java.util包中可用。 deleteObserver() method is used to remove the given observer (obs) from…

偶尔所得代码片(进程和锁相关)

--杀死相关进程&#xff08;必须要用dba账号sys&#xff09; --alter system kill session session_id, serial#;alter system kill session 500,2568;--select lower(CHR(64ROWNUM)) from dual connect by ROWNUM <126--查进程 select * from v$process; --查锁 select * f…

熬夜都要看完的 Spring 干货!

在 Java 后端框架繁荣的今天&#xff0c;Spring 无疑是最最最火热&#xff0c;也是必不可少的开源框架&#xff0c;像腾讯、阿里、字节跳动等一线互联网公司都选择 Spring 作为基础的开发框架。而 Spring 生态圈里最让人兴奋的莫过于 Spring Boot 框架。他简化了使用 Spring 的…

2014值得期待的Erlang两本新书

在2014年的开头就有这样一个令人振奋的好消息,Erlang有一本新书即将出版 《The Erlang Runtime System》,其作者happi在2013年3月份发布了这本书的写作计划:"The plan is to have the book done by the end of 2013 and published early 2014. ",出版方是O’Reilly,依…

页面分栏LayoutInflater

/* 页面分栏*/ public class TabDemo extends TabActivity {/** Called when the activity is first created. */Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TabHost tabHost getTabHost();LayoutInflater.from(this).inf…

这么简单的三目运算符,竟然这么多坑?

最近在一个业务改造中&#xff0c;使用三目运算符重构了业务代码&#xff0c;没想到测试的时候竟然发生 NPE 的问题。重构代码非常简单&#xff0c;代码如下&#xff1a;// 方法返回参数类型为 Integer // private Integer code; SimpleObj simpleObj new SimpleObj(); // 其…

Java 邮箱判断 正则表达式

import java.util.Scanner;public final class EmailCheck {public static boolean checkEmail(String email){String regex1 "[a-zA-Z][a-zA-Z0-9_]*[a-zA-Z0-9][.][a-zA-Z0-9]";//字母开头&#xff0c;后加字母或数字&#xff0c;后面加点&#xff0c;后面字母或数…

Java DataInputStream skipBytes()方法与示例

DataInputStream类skipBytes()方法 (DataInputStream Class skipBytes() method) skipBytes() method is available in java.io package. skipBytes()方法在java.io包中可用。 skipBytes() method is used to skip the given number of bytes of data from this DataInputStrea…

nagios客户端nrped服务方式启动脚本

1、平时配置nagios客户端nrped启动最常用的就是在/etc/rc.local文件配置&#xff1a;/usr/local/nagios/bin/nrpe -c /usr/local/nagios/etc/nrpe.cfg -d2、但是还有更好的方式&#xff08;这样方便使用脚本启动或者关闭&#xff09;&#xff1a;在/etc/init.d目录下创建nrped脚…