springboot 复制属性BeanUtils.copyProperties

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot 复制属性BeanUtils.copyProperties相关的知识,希望对你有一定的参考价值。

参考技术A InvoiceRequest 里面的两个属性是SrmFinanceGoodsSettlement 类里面已经存在的
测试复制1:

结果:

测试复制2:

结果:

invoiceRequest 中没有的属性不起作用,有的属性必定覆盖后一个,包括null

属性复制神器-mapstruct

我们之前说到项目中会用到各种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、其它

还有其它的高级功能,自行到官网探索

技术图片
推荐关注此文作者公众号:丰极,关注后回复“面试资料”即可获取百度阿里美团等大厂面试资料。



以上是关于springboot 复制属性BeanUtils.copyProperties的主要内容,如果未能解决你的问题,请参考以下文章

使用BeanUtils类实现DTO之间的属性复制

属性复制神器-mapstruct

使用BeanUtils复制Java对象

为啥阿里代码规约要求避免使用 Apache BeanUtils 进行属性复制

BeanUtils.copyProperties(A,B)字段复制

利用Java反射实现JavaBean对象相同属性复制并初始化目标对象为空的属性的BeanUtils