就因为加了Lombok的@Accessors(chain = true),bean拷贝工具类不干活了
前言
这次新建了一个工程,因为 Lombok 用得很习惯,但以前的话,一般只用了@Data,@AllArgsConstructor,@EqualsAndHashCode等常规注解;那这个Accessors(chain = true)注解是干嘛的呢?
用了这个注解后,生成的set方法是这样的:
#加了Accessors(chain = true)
public Devolution setCenterId(Long centerId) {
this.centerId = centerId;
return this;
}
注意,正常情况下,方法应该是下面这样的:
#没加Accessors(chain = true)
public void setCenterId(Long centerId) {
this.centerId = centerId;
}
为什么要用这个方法?主要是方便级联操作。基于这个考虑就加了。
加了后,出现了什么问题?
我们之前有个bean拷贝的工具类,用于在 po 和 vo 间拷贝属性。
import org.springframework.cglib.beans.BeanCopier;
public static void copyProperties(Object source,Object target){
BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
copier.copy(source, target, null);
}
结果,同事反映说,当target的类型,加了 Accessors(chain = true)时, 这个工具类不能用了!
跟踪问题
我本来以为改改spring源码就可以了,结果发现org.springframework.cglib.beans.BeanCopier 源码打不开,换了个spring 4的版本,也不行。看到包里面,是待了cglib的,于是本地找了个cglib的包,发现是带source的,于是解压后导入工程,嗯,还不错,可以用!

工程代码在:
https://gitee.com/ckl111/cglib-lombok-test
我这里先说问题原因:
我找到了一个测试用例,大概如下:
public void testSimple() {
BeanCopier copier = BeanCopier.create(MA.class, MA.class, false);
MA bean1 = new MA();
bean1.setIntP(42);
MA bean2 = new MA();
copier.copy(bean1, bean2, null);
assertTrue(bean2.getIntP() == 42);
}
然后自己改造了一下,加了个类:
@Data
@Accessors(chain = true)
class MaWithLombok {
private Long id;
private String name;
private String privateName;
private int intP;
private long longP;
private boolean booleanP;
private char charP;
private byte byteP;
private short shortP;
private float floatP;
private double doubleP;
private String stringP;
public String publicField;
}
这里是测试用例:
public void testSimpleLombok() {
BeanCopier copier = BeanCopier.create(MA.class, MaWithLombok.class, false);
MA bean1 = new MA();
bean1.setIntP(42);
MaWithLombok bean2 = new MaWithLombok();
copier.copy(bean1, bean2, null);
assertTrue(bean2.getIntP() == 42);
}
接下来,就是调试了,在不打断点直接run时,会抛下面异常:
java.lang.NullPointerException
at net.sf.cglib.core.ReflectUtils.getMethodInfo(ReflectUtils.java:424)
at net.sf.cglib.beans.BeanCopier$Generator.generateClass(BeanCopier.java:133)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.beans.BeanCopier$Generator.create(BeanCopier.java:90)
at net.sf.cglib.beans.BeanCopier.create(BeanCopier.java:50)
at net.sf.cglib.beans.TestBeanCopier.testSimpleLombok(TestBeanCopier.java:38)
打断点时,发现:

参数member为null,ok,把堆栈退一层(鼠标点到上一层frame)

然后寻找setter的来源:
PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target);
单步调试,会找到这个地方:
这里是进到了jdk的类,这里
java.beans.Introspector#getBeanInfo()
private BeanInfo getBeanInfo() throws IntrospectionException {
// the evaluation order here is import, as we evaluate the
// event sets and locate PropertyChangeListeners before we
// look for properties.
BeanDescriptor bd = getTargetBeanDescriptor();
MethodDescriptor mds[] = getTargetMethodInfo();
EventSetDescriptor esds[] = getTargetEventInfo();
PropertyDescriptor pds[] = getTargetPropertyInfo();//在这里,获取目标类的属性描述符列表
int defaultEvent = getTargetDefaultEventIndex();
int defaultProperty = getTargetDefaultPropertyIndex();
return new GenericBeanInfo(bd, esds, defaultEvent, pds,
defaultProperty, mds, explicitBeanInfo);
}
我们进入该方法,下图就能告诉你为什么(java/beans/Introspector.java:520):

原因总结
好了,经过上面的问题,大家能发现,因为我们注解的原因,导致setXXX方法的返回值不为void,所以使用
java.beans.Introspector#getTargetPropertyInfo来获取 PropertyDescriptor的时候,出现了问题。
问题解决
问题发现了,要怎么解决呢,也简单,我google了一下,哈哈哈。
参考:https://github.com/cglib/cglib/issues/108
使用下面这个工具方法即可:
org.springframework.beans.BeanUtils.copyProperties(source, target);
我的测试工程在,如果大家需要调试 cglib源码,也可以看看,里面有很多功能的test用例:
https://gitee.com/ckl111/cglib-lombok-test
就因为加了Lombok的@Accessors(chain = true),bean拷贝工具类不干活了的更多相关文章
- Mybatis 实体类使用@Accessors(chain = true)注解时,对应的mapper xml 报错
去掉这个注解就行了 应该是 mybatis 会调用实体类的 getter setter 方法, 返回值可能会有所影响
- lombok的@Accessors注解
@AllArgsConstructor @Data @NoArgsConstructor @Accessors(chain = true) @EqualsAndHashCode public clas ...
- lombok的@Accessors注解3个属性说明
https://www.cnblogs.com/kelelipeng/p/11326936.html https://www.cnblogs.com/kelelipeng/p/11326621.htm ...
- lombok使用指南,代码极简工具
我们的项目中会用到各种bean,比如vo,bo,dto等等,bean上的属性我们一般写get(),set()方法,整个java文件看起来很臃肿. 一.简介 我们今天介绍的lombok只用使用注解就可以 ...
- android html 图片处理类--加载富文本工具类
在android开发中,一些资讯类页面,里面有html标签和图片,html 标签一般通过Html.fromHtml方法,即可以解决,但是如果html 有图片标签,那么,Html.fromHtml 好像 ...
- velocity merge作为工具类从web上下文和jar加载模板的两种常见情形
很多时候,处于各种便利性或折衷或者通用性亦或是限制的原因,会借助于模板生成结果,在此介绍两种使用velocity merge的情形,第一种是和spring mvc一样,将模板放在velocityCon ...
- Json工具类,实现了反射将整个Object转换为Json对象的功能,支持Hibernate的延迟加
package com.aherp.framework.util; import java.lang.reflect.Array;import java.lang.reflect.Method;imp ...
- Android加载网络图片的工具类
ImageView加载网络的图片 HttpUtil.java package com.eiice.httpuimagetils; import java.io.ByteArrayOutputStrea ...
- Android自定义圆形图片工具类(CTRL+C加CTRL+V直接使用)
先贴一下工具类的代码!可直接复制粘贴 public class RoundImageView extends ImageView { private Paint mPaint; //画笔 privat ...
随机推荐
- openstack问题记录
先去查看对应的日志:/var/log/,再来排查错误 1.实例处于错误状态 解决办法: 1.使用openstack hypervisor list查看 2.然后openstack hypervisor ...
- ES6学习总结之Set和Map数据结构的理解
前言 当我们需要存储一些数据的时候,首先想到的是定义一个变量用来存储,之后我们可能学了数组,发现数组比变量可以存储更多的数据,接着可能有其它的存储数据的方法等等,然而我今天需要介绍的是在ES6中比较常 ...
- 排坑日记之批量从库IO进程停止
早上刚睁眼,看到了一堆数据库告警的短信,其中一个内容如下: Problem started at 05:02:58 on 2019.10.12 Problem name: Slave is stopp ...
- Android Studio 优秀插件:GsonFormat
作为一个Android程序猿,当你看到后台给你的json数据格式时: { "id":123, "url": "http://img.donever.c ...
- (4)一起来看下mybatis框架的缓存原理吧
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了 我们知道.使用缓存可以更快的获取数据,避免频繁直接查询数据库,节省资源. MyBatis ...
- python编程基础之十六
for in 循环,与其说是循环不如说精确点交遍历 for 变量名 in + 迭代对象 语句A else: 语句B 作用:一次访问迭代对象中的元素并赋值给变量 循环终止时,执行else语句块,如果br ...
- react-native开发经验
# **RN开发经验** ## 一.环境配置关于环境配置,前辈已有完整的总结:http://tvrn.devops.letv.com/docs/Environment.html **IDE准备:** ...
- 基于STM32F1与NRF24L01模块的SPI简单通信
一.前言 1.简介: 本文是基于STM32F1,将数据发送至NRF模块的寄存器,并将数据重新读取,通过串口发送出来的简单SPI单通信. 2.SPI简介: 调过STM8的都已经对SPI有所了解,调法都一 ...
- [WPF自定义控件库] 模仿UWP的ProgressRing
1. 为什么需要ProgressRing 虽然我认为这个控件库的控件需要模仿Aero2的外观,但总有例外,其中一个就是ProgressRing.ProgressRing是来自UWP的控件,部分代码参考 ...
- springboot依赖的一些配置:spring-boot-dependencies、spring-boot-starter-parent、io.spring.platform
springboot里会引入很多springboot starter依赖,这些依赖的版本号统一管理,springboot有几种方案可以选择. 一.spring-boot-dependencies 有两 ...