技术的更新很快,曾经几何时,还在纠结数据库连接池是c3p0还是dbcp,反复测试哪个更快。再到后来BoneCP、Druid出世,Druid占据了配置里好长时间。现如今,HikariCP秒杀了一切。充分体现了技术人员孜孜不倦的对性能和效率追求,是没有终点的。
Java的Bean对象属性拷贝的是个很小的功能点,一直以来都是BeanUtil的copyProperties,无论是用的commmons-utils还是spring包里的。
目前这领域的技术叫做映射器(Object mapping),技术分成两类:
1、使用反射技术调用拿到属性的set/get方法通过invoke赋值 。 这类的代表:commons-beanutils,Dozer,ModelMaper。
2、编译期动态生成调用set/get代码的class文件。 这类的代表:MapStruct,Selma,Orika。
使用反射技术实现的对象拷贝和使用动态编译生成字节码的技术,性能上不具可比性,基本是被吊打。
下面是一张来自网上性能测试对比图,每秒操作数,越高越好,Manual是手工set/get的指标。
使用上来说,动态生成字节码,编写调用时,必须有个stub,一般会要求先编写一个接口,在这个基础上做一些属性映射上的设置。目前推荐的是MapStruct这个库,虽然Selma这个有着更为接近手写set的拷贝性能,但是考虑到它的项目已经好多年未更新了。
可以看一下官网首页的样例代码:
先定义映射器接口:
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
然后使用它拷贝指定的bean:
@Test
public void shouldMapCarToDto() {
//given
Car car = new Car( "Morris", 5, CarType.SEDAN );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
//then
assertThat( carDto ).isNotNull();
assertThat( carDto.getMake() ).isEqualTo( "Morris" );
assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}
看起来,精准、安全、高效。但我估计用起来会有点累。都动态生成代码了,还非得写一个接口是干嘛。
期待更好的方案。如果不考虑性能,我还是会使用BeanUtil,那样更轻松些。
附: