发现Mapstruct的一个bug
前言
在一次需求的开发过程中,发现一个对象插入数据库时某个字段总是为空。简化后的代码如下:
@Autowired
private PersonService personService;
public void test1(){
Person person = personService.findById(1L);
PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);
personService.insert(personDto);
}
这么简单的逻辑按理说不会出幺蛾子啊,我先排查了数据库里person id=1的记录发现值是有的啊,然后又排查了我的insert方法,也是没问题的。
经过一段时间的排查,才发现是
PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);
这行代码的问题。证据截图如下:
前面的时候addTeacherNum还有值,转化后怎么又没值了呢?
大家看到这里肯定猜测是不是我属性名不对,或者属性类型不对。我甚至还删除了之后用复制的方式来保证没有手敲敲错的情况。
完全是一模一样的属性啊。
我们知道mapstruct是编译时通过我们的PersonMapper接口来实现实现类,实现类里是setter、getter方法来实现的。于是我打开了PersonMapper的实现类准备一探究竟:
public class PersonMapperImpl implements PersonMapper {
public PersonMapperImpl() {
}
public PersonDto personToPersonDto(Person person) {
if (person == null) {
return null;
} else {
PersonDtoBuilder personDto = PersonDto.builder();
personDto.name(person.getName());
return personDto.build();
}
}
}
竟然没有对我这个属性addTeacherNum进行赋值。这让我百思不得其解。只能去看看源码,试图找出原因。
如何调试Maven插件
前面我们提到mapstruct是在代码编译的时候就开始生成代码了,于是我们需要对maven编译期进行调试。方法如下:
- maven debug命令
mvnDebug clean compile
- idea远程debug
新建一个remote,然后修改端口为8000,然后在执行maven命令的同时,启动这个remote即可。
源码解析
断点应该打在哪里呢?
我们查看mapstruct的结构,一般先从配置的文件入手
找到了这个MappingProcessor类,我们可以看到这里面有个process方法,里面又调用了如下的这个方法:
private void processMapperTypeElement(ProcessorContext context, TypeElement mapperTypeElement) {
Object model = null;
for ( ModelElementProcessor<?, ?> processor : getProcessors() ) {
try {
model = process( context, processor, mapperTypeElement, model );
}
catch ( AnnotationProcessingException e ) {
//省略
}
}
}
这段代码其实就是调用getProcessors()方法拿到多个processor,然后遍历调用。而这个getProcessors()就是从配置文件里通过SPI的方式加载对象。
这里面我们重点关注这个Processor:MapperCreationProcessor。它的process方法如下:
@Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<SourceMethod> sourceModel) {
this.elementUtils = context.getElementUtils();
this.typeUtils = context.getTypeUtils();
this.messager =
new MapperAnnotatedFormattingMessenger( context.getMessager(), mapperTypeElement, context.getTypeUtils() );
this.options = context.getOptions();
this.versionInformation = context.getVersionInformation();
this.typeFactory = context.getTypeFactory();
this.accessorNaming = context.getAccessorNaming();
MapperOptions mapperOptions = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() );
List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement, mapperOptions );
MappingBuilderContext ctx = new MappingBuilderContext(
typeFactory,
elementUtils,
typeUtils,
messager,
accessorNaming,
context.getEnumMappingStrategy(),
context.getEnumTransformationStrategies(),
options,
new MappingResolverImpl(
messager,
elementUtils,
typeUtils,
typeFactory,
new ArrayList<>( sourceModel ),
mapperReferences,
options.isVerbose()
),
mapperTypeElement,
//sourceModel is passed only to fetch the after/before mapping methods in lifecycleCallbackFactory;
//Consider removing those methods directly into MappingBuilderContext.
Collections.unmodifiableList( sourceModel ),
mapperReferences
);
this.mappingContext = ctx;
return getMapper( mapperTypeElement, mapperOptions, sourceModel );
}
getMapper里面有一段这个方法引起我的注意:
List<MappingMethod> mappingMethods = getMappingMethods( mapperOptions, methods );
猜测这段就是获取要写入的set、get方法。
于是一路跟踪:
发现mapstruct里面把方法分为了下面四类,而我的addTeacherNum属性通过lombok生成的方法methodType被分到了ADDER里面。
而在生成我的Mapper实现类的时候它会只过滤setter方法。
List<Accessor> candidates = new ArrayList<>( getSetters() );
至此真相大白。
发现Mapstruct的一个bug的更多相关文章
- 发现IE6的一个BUG,添加受信任站点后,页面无法跳转
最近客户爆了一个问题,说是最近使用我们的系统,一登录浏览器就直接关闭了. 经排查,属于IE6设置受信任站点的问题,受信任站点设置了通配符,如 http://192.168.1.* 这样的格式,而我 ...
- 发现护考上机考试的一个bug:附软件截图(模拟软件)
目录: 一.文章主旨 二.问题发现的起因 三.bug(问题)描述 四.软件截图 五.我的思考 六.一点期盼 一.文章主旨: 2019年5月18.19.20日,又是一年一度的护资考试(上机考),考试前夕 ...
- Tomcat一个BUG造成CLOSE_WAIT
之前应该提过,我们线上架构整体重新架设了,应用层面使用的是Spring Boot,前段日子因为一些第三方的原因,略有些匆忙的提前开始线上的内测了.然后运维发现了个问题,服务器的HTTPS端口有大量的C ...
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- Win10系统菜单打不开问题的解决,难道是Win10的一个Bug ?
Win10左下角菜单打不开,好痛苦,点击右下角的时间也没反应,各种不爽,折磨了我好几天,重装又不忍心,实在费劲,一堆开发环境要安装,上网找了很多方法都不适用.今天偶然解决了,仔细想了下,难道是Win1 ...
- 一次发现underscore源码bug的经历以及对学术界『拿来主义』的思考
事情是如何发生的 最近干了件事情,发现了 underscore 源码的一个 bug.这件事本身并没有什么可说的,但是过程值得我们深思,记录如下,各位看官仁者见仁智者见智. 平时有浏览园区首页文章的习惯 ...
- 你可能不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG
这篇文章并不在我的 underscore 源码解读计划中,直到 @pod4g 同学回复了我的 issue(详见 https://github.com/hanzichi/underscore-analy ...
- 关于MySQL count(distinct) 逻辑的一个bug【转】
本文来自:http://dinglin.iteye.com/blog/1976026#comments 背景 客户报告了一个count(distinct)语句返回结果错误,实际结果存在值,但是用cou ...
- 微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug
开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配 ...
随机推荐
- go-micro使用Consul做服务发现的方法和原理
go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...
- WIN10 使用POWERSHELL 设置单应用KIOSK模式(win10家庭版或企业版)
win10 使用PowerShell 设置单应用kiosk模式 win10 家版或企业版PowerShellshell 启动器 v1Autologon.exe 注意事项 win10 家庭版或企业版. ...
- [笔记] Slope Trick:解决一类凸代价函数的DP优化问题
原理 当序列 DP 的转移代价函数满足 连续: 凸函数: 分段线性函数. 时,可以通过记录分段函数的最右一段 \(f_r(x)\) 以及其分段点 \(L\) 实现快速维护代价的效果. 如:$ f(x) ...
- 干货 | LVM快照学习
一个执着于技术的公众号 前言 在上一章节,我们学习了LVM逻辑卷管理技术,知道了LVM能够通过增减PE的数量来弹性调整文件系统的大小.除此之外,LVM还有另一个重要功能「LVM快照技术」,也就是可以给 ...
- 使用CSS实现《声生不息》节目Logo
声明:本文涉及图文和模型素材仅用于个人学习.研究和欣赏,请勿二次修改.非法传播.转载.出版.商用.及进行其他获利行为. 背景 <声生不息> 是芒果TV.香港电视广播有限公司和湖南卫视联合推 ...
- 流量治理神器-Sentinel 究竟是怎么做到让业务方接入简单?
大家好,我是架构摆渡人,这是流量治理系列的第10篇原创文章,如果有收获,还请分享给更多的朋友. 做业务开发,需要考虑业务的扩展性.做基础框架开发,需要考虑如何让业务方接入,使用简单,尽量不要耦合在业务 ...
- unity---射线
射线 Ray ray= Camera.main.ScreenPointToRay(Input.mousePosition);//获得鼠标射向的射线 RaycastHit hit;//射线碰撞物体 if ...
- k8s的api资源
NAME SHORTNAMES APIGROUP NAMESPACED KIND 资源用途说明 bindings TRUE Binding 已弃用.用于记录一个object和另一个object ...
- 微信小程序开发 记录
采坑了 微信小程序--TabBar不出现的一种原因 学习微信小程序中,遇到底部的TabBar不出现的问题.经过多番尝试,终于解决问题.在此记录问题产生的原因和对策.下面先描述错误现象,接着指出错误原因 ...
- WC2019
好题啊! 数树 \(\text{opt = 0, 6 pts.}\) 显然答案为 \(y^{n-|E_1∩E_2|}\) . \(\text{opt = 1, 47 pts.}\) \[\sum_{E ...