我們之前說到項目中會用到各種object,vo,bo,dto等等。我們需要在不同的對象上復制屬性。
一、BeanUtils和PropertyUtils
我們最常用的就是Common包里面的BeanUtils,或者Spring里面的BeanUtils.
BeanUtils.copyProperties(dest, orig);
還有一個PropertyUtils
PropertyUtils.copyProperties(dest, orig);
它倆區別:
-
BeanUtils和PropertyUtils復制對象時,根據屬性名進行復制。
-
如果屬性名相同,但類型不同,BeanUtils會直接轉換; 而PropertyUtils會直接拋出異常。
二、自己寫轉換Builder
上面的BeanUtils和PropertyUtils代碼寫起來很方便簡潔,但是它的實現原理是基於反射,效率不是很高。如果是對服務響應要求很高的應用,用它們就不太合適。有一種方法,自己寫轉換關系,一個一個set。
public class PersonBuilder {
public static Person newPerson(Context context) {
Person person = new Person();
person.setDelRepeat(context.getDelRepeat());
person.setSendName(context.getSendName());
person.setSendType(context.getSendType());
person.setInputMdnList(context.getInputMdnList());
person.setSceneId(context.getSceneId());
person.setGroupIdList(context.getGroupIdList());
person.setContent(context.getContent());
person.setImportFile(context.getImportFile());
return person;
}
public static PersonVO newPersonVO(Context context) {
... ...
這種方法沒有性能問題,但是效率不高,字段少的話工作量還行,如果字段很多,會寫的很頭疼
三、mapStruct
mapStruct的原理是基於第二種方法,但是它只用寫一個接口和方法,會自動生成實現類,工作量和第一種相當,效率和第二種方法一樣。
1、舉個官方的例子
引入maven依賴包
...
<properties>
<org.mapstruct.version>1.4.0.Beta2</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
定義Car.java
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
//constructor, getters, setters etc.
}
定義CarDto.java
public class CarDto {
private String make;
private int seatCount;
private String type;
//constructor, getters, setters etc.
}
定義mapper
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
使用mapper
@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" );
}
2、更新
mapStruct還可以更新已有的bean
@Mapper
public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}
3、支持數據類型轉換
比如int轉String
@Mapper
public interface CarMapper {
@Mapping(source = "price", numberFormat = "$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat = "$#.00")
List<String> prices(List<Integer> prices);
}
4、支持map,set,list
public interface SourceTargetMapper {
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
@Mapper
public interface CarMapper {
Set<String> integerSetToStringSet(Set<Integer> integers);
List<CarDto> carsToCarDtos(List<Car> cars);
CarDto carToCarDto(Car car);
}
5、支持Stream
@Mapper
public interface CarMapper {
Set<String> integerStreamToStringSet(Stream<Integer> integers);
List<CarDto> carsToCarDtos(Stream<Car> cars);
CarDto carToCarDto(Car car);
}
6、自定義origin和dest的名稱
@Mapper
public interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
@ValueMappings({
@ValueMapping(source = "EXTRA", target = "SPECIAL"),
@ValueMapping(source = "STANDARD", target = "DEFAULT"),
@ValueMapping(source = "NORMAL", target = "DEFAULT")
})
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
7、支持自定義的工廠模式生成bean
用這種方式就不會new,而是用工廠的createXXX方法
public class DtoFactory {
public CarDto createCarDto() {
return // ... custom factory logic
}
}
public class EntityFactory {
public <T extends BaseEntity> T createEntity(@TargetType Class<T> entityClass) {
return // ... custom factory logic
}
}
@Mapper(uses= { DtoFactory.class, EntityFactory.class } )
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
CarDto carToCarDto(Car car);
Car carDtoToCar(CarDto carDto);
}
8、其它
還有其它的高級功能,自行到官網探索
推薦關注此文作者公眾號:豐極,關注后回復“面試資料”即可獲取百度阿里美團等大廠面試資料。