2024年最全Java对象转换方案分析与mapstruct实践,内容非常全面

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

3 beanCopier

BeanCopier copier = BeanCopier.create(CarDO.class, CarDTO.class, false);

copier.copy(do, dto, null);

这种方案动态生成一个要代理类的子类,其实就是通过字节码方式转换成性能最好的get和set方式,重要的开销在创建BeanCopier,整体性能接近原生代码处理,比BeanUtils要好很多,尤其在数据量很大时,但是针对复杂场景支持能力不足。

4 各种Mapping框架

分类

Object Mapping 技术从大的角度来说分为两类,一类是运行期转换,另一类则是编译期转换:

  • 运行期反射调用 set/get 或者是直接对成员变量赋值。这种方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。运行期对象转换的代表主要是Dozer和ModelMaper。

  • 编译期动态生成 set/get 代码的class文件,在运行时直接调用该class的 set/get 方法。该方式实际上仍会存在 set/get 代码,只是不需要开发人员自己写了。这类的代表是:MapStruct,Selma,Orika。

分析

  • 无论哪种Mapping框架,基本都是采用xml配置文件 or 注解的方式供用户配置,然后生成映射关系。

  • 编译期生成class文件方式需要DTO仍然有set/get方法,只是调用被屏蔽;而运行期反射方式在某些直接填充 field的方案中,set/get代码也可以省略。

  • 编译期生成class方式会有源代码在本地,方便排查问题。

  • 编译期生成class方式因为在编译期才出现java和class文件,所以热部署会受到一定影响。

  • 反射型由于很多内容是黑盒,在排查问题时,不如编译期生成class方式方便。参考GitHub上工程java-object-mapper-benchmark可以看出主要框架性能比较。

  • 反射型调用由于是在运行期根据映射关系反射执行,其执行速度会明显下降N个量级。

  • 通过编译期生成class代码的方式,本质跟直接写代码区别不大,但由于代码都是靠模板生成,所以代码质量没有手工写那么高,这也会造成一定的性能损失。

Java对象转换方案分析与mapstruct实践

综合性能、成熟度、易用性、扩展性,mapstruct是比较优秀的一个框架。

三 Mapstruct使用指南

1 Maven引入

Java对象转换方案分析与mapstruct实践

2 简单入门案例

DO和DTO

这里用到了lombok简化代码,lombok的原理也是在编译时去生成get、set等被简化的代码。

@Data

public class Car {

private String make;

private int numberOfSeats;

private CarType type;

}

@Data

public class CarDTO {

private String make;

private int seatCount;

private String type;

}

定义Mapper

@Mapper中描述映射,在编辑的时候mapstruct将会根据此描述生成实现类:

  • 当属性与其目标实体副本同名时,它将被隐式映射。

  • 当目标实体中的属性具有不同名称时,可以通过@Mapping注释指定其名称。

@Mapper

public interface CarMapper {

@Mapping(source = “numberOfSeats”, target = “seatCount”)

CarDTO CarToCarDTO(Car car); }

使用Mapper

通过Mappers 工厂生成静态实例使用。

@Mapper

public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = “numberOfSeats”, target = “seatCount”)

CarDTO CarToCarDTO(Car car);

}

Car car = new Car(…);

CarDTO carDTO = CarMapper.INSTANCE.CarToCarDTO(car);

getMapper会去load接口的Impl后缀的实现类。

Java对象转换方案分析与mapstruct实践

通过生成spring bean注入使用,Mapper注解加上spring配置,会自动生成一个bean,直接使用bean注入即可访问。

@Mapper(componentModel = “spring”)

public interface CarMapper {

@Mapping(source = “numberOfSeats”, target = “seatCount”)

CarDTO CarToCarDTO(Car car);

}

自动生成的MapperImpl内容

如果配置了spring bean访问会在注解上自动加上@Component。

Java对象转换方案分析与mapstruct实践

3 进阶使用

逆向映射

如果是双向映射,例如 从DO到DTO以及从DTO到DO,正向方法和反向方法的映射规则通常是相似的,并且可以通过切换源和目标来简单地逆转。

使用注解@

InheritInverseConfiguration 指示方法应继承相应反向方法的反向配置。

@Mapper

public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = “numberOfSeats”, target = “seatCount”)

CarDTO CarToCarDTO(Car car);

@InheritInverseConfiguration

Car CarDTOToCar(CarDTO carDTO);

}

更新bean映射

有些情况下不需要映射转换产生新的bean,而是更新已有的bean。

@Mapper

public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = “numberOfSeats”, target = “seatCount”)

void updateDTOFromCar(Car car, @MappingTarget CarDTO carDTO);

集合映射

集合类型(List,Set,Map等)的映射以与映射bean类型相同的方式完成,即通过在映射器接口中定义具有所需源类型和目标类型的映射方法。MapStruct支持Java Collection Framework中的多种可迭代类型。

生成的代码将包含一个循环,该循环遍历源集合,转换每个元素并将其放入目标集合。如果在给定的映射器或其使用的映射器中找到用于集合元素类型的映射方法,则将调用此方法以执行元素转换,如果存在针对源元素类型和目标元素类型的隐式转换,则将调用此转换。

@Mapper

public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = “numberOfSeats”, target = “seatCount”)

CarDTO CarToCarDTO(Car car);

List carsToCarDtos(List cars);

Set integerSetToStringSet(Set integers);

@MapMapping(valueDateFormat = “dd.MM.yyyy”)

Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);

}

编译时生成的实现类:

Java对象转换方案分析与mapstruct实践

多个源参数映射

MapStruct 还支持具有多个源参数的映射方法。例如,将多个实体组合成一个数据传输对象。

在原案例新增一个Person对象,CarDTO中新增driverName属性,根据Person对象获得。

@Mapper

public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = “car.numberOfSeats”, target = “seatCount”)

@Mapping(source = “person.name”, target = “driverName”)

CarDTO CarToCarDTO(Car car, Person person); }

编译生成的代码:

Java对象转换方案分析与mapstruct实践

默认值和常量映射

如果相应的源属性是null ,则可以指定默认值以将预定义值设置为目标属性。在任何情况下,都可以指定常量来设置这样的预定义值。默认值和常量被指定为字符串值。当目标类型是原始类型或装箱类型时,String 值将采用字面量,在这种情况下允许位/八进制/十进制/十六进制模式,只要它们是有效的文字即可。在所有其他情况下,常量或默认值会通过内置转换或调用其他映射方法进行类型转换,以匹配目标属性所需的类型。

@Mapper

public interface SourceTargetMapper {

SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

@Mapping(target = “stringProperty”, source = “stringProp”, defaultValue = “undefined”)

@Mapping(target = “longProperty”, source = “longProp”, defaultValue = “-1”)

@Mapping(target = “stringConstant”, constant = “Constant Value”)

@Mapping(target = “integerConstant”, constant = “14”)

@Mapping(target = “longWrapperConstant”, constant = “3001”)

@Mapping(target = “dateConstant”, dateFormat = “dd-MM-yyyy”, constant = “09-01-2014”)

@Mapping(target = “stringListConstants”, constant = “jack-jill-tom”)

Target sourceToTarget(Source s);

}

自定义映射方法或映射器

在某些情况下,可能需要手动实现 MapStruct 无法生成的从一种类型到另一种类型的特定映射。

可以在Mapper中定义默认实现方法,生成转换代码将调用相关方法:

@Mapper

public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = “numberOfSeats”, target = “seatCount”)

@Mapping(source = “length”, target = “lengthType”)

CarDTO CarToCarDTO(Car car);

default String getLengthType(int length) {

if (length > 5) {

return “large”;

} else {

return “small”;

}

}

}

也可以定义其他映射器,如下案例Car中Date需要转换成DTO中的String:

public class DateMapper {

public String asString(Date date) {

return date != null ? new SimpleDateFormat( “yyyy-MM-dd” ).format( date ) : null;

}

public Date asDate(String date) {

写在最后

作为一名即将求职的程序员,面对一个可能跟近些年非常不同的 2019 年,你的就业机会和风口会出现在哪里?在这种新环境下,工作应该选择大厂还是小公司?已有几年工作经验的老兵,又应该如何保持和提升自身竞争力,转被动为主动?

就目前大环境来看,跳槽成功的难度比往年高很多。一个明显的感受:今年的面试,无论一面还是二面,都很考验Java程序员的技术功底。

最近我整理了一份复习用的面试题及面试高频的考点题及技术点梳理成一份“Java经典面试问题(含答案解析).pdf和一份网上搜集的“Java程序员面试笔试真题库.pdf”(实际上比预期多花了不少精力),包含分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货!

由于篇幅有限,为了方便大家观看,这里以图片的形式给大家展示部分的目录和答案截图!

Java经典面试问题(含答案解析)

阿里巴巴技术笔试心得

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

码分析,Redis,ActiveMQ、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货!**

由于篇幅有限,为了方便大家观看,这里以图片的形式给大家展示部分的目录和答案截图!
[外链图片转存中…(img-t2uO5aZx-1715080171693)]

Java经典面试问题(含答案解析)

[外链图片转存中…(img-R0dyU4PP-1715080171694)]

阿里巴巴技术笔试心得

[外链图片转存中…(img-UpPUpDFR-1715080171694)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值