PropertyMapper使用


PropertyMapper是Spring提供的一个工具类,主要用于重新赋值,转换等操作,位于org.springframework.boot.context.properties.PropertyMapper下。

我第一次发现这个类是在spring-boot-starter-amqp中,配置RabbitTemplate中看到的

代码位于org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.RabbitTemplateConfiguration

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(RabbitOperations.class)
public RabbitTemplate rabbitTemplate(RabbitProperties properties,
                                     ObjectProvider<MessageConverter> messageConverter,
                                     ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers,
                                     ConnectionFactory connectionFactory) {
    PropertyMapper map = PropertyMapper.get();
    RabbitTemplate template = new RabbitTemplate(connectionFactory);
    messageConverter.ifUnique(template::setMessageConverter);
    template.setMandatory(determineMandatoryFlag(properties));
    RabbitProperties.Template templateProperties = properties.getTemplate();
    if (templateProperties.getRetry().isEnabled()) {
        template.setRetryTemplate(
            new RetryTemplateFactory(retryTemplateCustomizers.orderedStream().collect(Collectors.toList()))
            .createRetryTemplate(templateProperties.getRetry(),
                                 RabbitRetryTemplateCustomizer.Target.SENDER));
    }
    map.from(templateProperties::getReceiveTimeout).whenNonNull().as(Duration::toMillis)
        .to(template::setReceiveTimeout);
    map.from(templateProperties::getReplyTimeout).whenNonNull().as(Duration::toMillis)
        .to(template::setReplyTimeout);
    map.from(templateProperties::getExchange).to(template::setExchange);
    map.from(templateProperties::getRoutingKey).to(template::setRoutingKey);
    map.from(templateProperties::getDefaultReceiveQueue).whenNonNull().to(template::setDefaultReceiveQueue);
    return template;
}

那一个个的 from….to…让我很好奇,于是便研究了一下他的用法。

在实际工作中,经常会遇到将数据库的实体类Entity转成DTO类的操作,通常情况。我们有可以将属性一个个get出来,在set进去。还有一种就是用BeanUtils工具类将对应类型的属性一个个copy进去。

了解了PropertyMapper之后,我们有多了一种方法转换了。

先建立两个类,Order和OrderDTO

Order.java

@Data
public class Order {
    private Long id;

    private BigDecimal totalAmout;

    private Integer status;

    private Long userId;

    private LocalDateTime createTime;
}

OrderDTO.java

@Data
public class OrderDTO {
    private Long id;

    private BigDecimal totalAmout;

    private Integer status;

    private Long userId;

    private String createTime;
}

使用PropertyMapper转换

Order order = new Order();
order.setId(1L);
order.setStatus(1);
order.setTotalAmout(BigDecimal.ONE);
order.setUserId(100L);
order.setCreateTime(LocalDateTime.now());

PropertyMapper propertyMapper = PropertyMapper.get();
OrderDTO orderDTO = new OrderDTO();
propertyMapper.from(order::getId).to(orderDTO::setId);
// 如果from获取到的元素不是null,则执行to里面的动作
propertyMapper.from(order::getStatus).whenNonNull().to(orderDTO::setStatus);
propertyMapper.from(order::getUserId).to(orderDTO::setUserId);
propertyMapper.from(order::getTotalAmout).to(orderDTO::setTotalAmout);
// 因为Order里面的createTime是LocalDateTime类型,OrderDTO里面则是String类型,需要转换一下
propertyMapper.from(order::getCreateTime).as(createTime -> {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    return createTime.format(formatter);
}).to(orderDTO::setCreateTime);

这样一来就可以通过PropertyMapper将Order对象的值set到OrderDTO对象中。

PropertyMapper常用方法

  • <T> Source<T> from(Supplier<T> supplier):提供值的来源,入参为Supplier
  • <T> Source<T> from(T value):一种重载形式,入参可以为一个对象
  • void to(Consumer<T> consumer):通过将任何未过滤的值传递给指定的使用者来完成映射
  • <R> R toInstance(Function<T, R> factory):通过从未过滤的值创建新实例来完成映射
  • void toCall(Runnable runnable):当值还没有时,通过调用指定的方法来完成映射
  • <R> Source<R> as(Function<T, R> adapter):将T类型的入参转成R类型的出参,类似于Stream中的map
  • Source<T> when...:这一系列方法,都是过滤用的。在from后面调用,如果满足条件,就直接to方法
  • static PropertyMapper get():提供PropertyMapper实例
  • PropertyMapper alwaysApplyingWhenNonNull():提供实例时,当前实例就过滤掉from之后是null的元素。PropertyMapper.get().alwaysApplyingWhenNonNull();
  • PropertyMapper alwaysApplying(SourceOperator operator):自定义过滤规则,参考代码
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplying(new PropertyMapper.SourceOperator() {
            @Override
            public <T> PropertyMapper.Source<T> apply(PropertyMapper.Source<T> source) {
                return source.when(t -> t instanceof LocalDateTime);
            }
        });

就好比,当前 PropertyMapper只会映射 LocalDateTime类型的字段。

注意:如果from方法后面有when条件,则alwaysApplying中设置的初始化提交将会失效。

Spring里面还有各式各样的类似这样的高逼格,方便使用的小工具。多多阅读Spring源码,就可以发现好多。


Author: Re:0
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Re:0 !
 Previous
外观模式(门面模式) 外观模式(门面模式)
定义本小节我们要学习的设计模式叫做外观模式,也叫做门面模式 Facade。想象一下,我们系统随着时间的推移,系统复杂性、类之间的相互调用会变得越来越多,相比较客户角度而言,客户往往关注的是某个单一接口 API,而不会关心该 API 内部的复
2022-03-05
Next 
Spring Boot国际化 Spring Boot国际化
让国际友人也能快乐的访问的你的服务 spring-boot-starter-web里面有国际化需要的API,因此我们不需要引入其他的API了。
2022-02-26
  TOC