就因为加了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 ...
随机推荐
- 使用 Jenkins 与 Sonar 集成对代码进行持续检测
SonarQube 与 Jenkins 简介 SonarQube是 一个开源的代码质量分析平台,便于管理代码的质量,可检查出项目代码的漏洞和潜在的逻辑问题.同时,它提供了丰富的插件,支持多种语言的检测 ...
- TinyXML2的快速实践
最近遇到个需要在C++中处理XML文件的需求,虽然对此方面并不是很熟,但好在有GitHub上的awesome-cpp项目的帮助,还是收获了足够的相关知识. 类库 常用的或被推荐的XML类库有以下数个选 ...
- 《Java语言程序设计》编程练习6.31(财务应用程序:信用卡号的合法性)
6.31(财务应用程序:信用卡号的合法性)信用卡号遵循下面的模式.一个信用卡号必须是13到16位的整数.它的开头必须是: 4,指Visa卡 5,指Master卡 37,指American Expres ...
- python编程基础之三十五
系统的魔术方法:系统的魔术方法特别多,但是也都特别容易懂,简单的讲就是对系统的内置函数进行重写,你需要什么效果就重写成什么样, 比如说len()方法针对的对象本来没有自定义类的对象,但是当你重写了__ ...
- spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
@Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供 ...
- Mac搭建 Eclipse +Pydev+Python 环境
Mac配置Python开发环境(Eclipse +Pydev+Python) 1.首先下载MAC版的64位Eclipse. eclips下载地址打开链接,选择需要的版本下载 2.下载Python. M ...
- python程序编译成exe文件
最近越来越喜欢使用python写工具.使用的时候,发现程序内部成员python安装目录常常不同,如果用bat双击执行,常常需要修改从svn上down下来的bat文件中python.exe的路径.而给策 ...
- X-扫描线算法
多边形的扫描转换 多边形有两种重要的表示方法:顶点表示和点阵表示 顶点表示是用多边形的顶点序列来表示多边形.这种表示直观.几何意义强.占内存少,易于进行几何变换. 但由于它没有明确指出哪些象素在多边形 ...
- opencv::点多边形测试
点多边形测试 测试一个点是否在给定的多边形内部,边缘或者外部 double pointPolygonTest( InputArray contour, // 输入的轮廓 Point2f pt, // ...
- Linux::mysql-connector-c++
.安装好boost. .从官网下载mysql connector c++版本. .解压,复制 include/jdbc/cppconn 文件夹复制,到/usr/local/include/cppcon ...