[Java/Spring] 深入理解 : SpringBoot PropertyMapper

1 概述: SpringBoot PropertyMapper

简介:PropertyMapper ∈ 对象拷贝与转换工具

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

辨析: Spring BeanUtils 与 SpringBoot PropertyMapper

  • 共同点:
  • 对象及属性拷贝工具:BeanUtils 和 PropertyMapper 都是 Spring 框架中用于处理 Java Bean 之间对象属性拷贝的工具。
  • 不同点:
  • 通用 vs 定制 :BeanUtils提供了拷贝属性的通用方法,而PropertyMapper提供了一种更灵活、可扩展的方式来定制属性的映射逻辑。
  • 对于BeanUtils.copyProperties来说,你必须保证属性名类型是相同的,因为它是根据get和set方法来赋值的。
  • 浅拷贝 vs 深拷贝:
  • BeanUtils :浅拷贝

org.springframework.beans.BeanUtils 工具类中的 copyProperties() 无法实现深拷贝,只能实现浅拷贝
详情参见:

  • PropertyMapper : 支持深拷贝,完全取决于应用程序的开发者用户的诉求
  • 模块/包
  • BeanUtils : spring-beans 模块
  • org.springframework.beans.BeanUtils
  • PropertyMapper : spring-boot 模块
  • org.springframework.boot.context.properties.PropertyMapper

2 应用场景

场景 :2个异构数据对象的拷贝与转换

  • 在实际工作中,经常会遇到将数据库的实体类 Entity 转成 DTO 类的操作。通常的方法:
  • 手工方法:我们有可以将属性一个个get出来,再set进去。

但经常涉及到判空、数据类型的转换等简单的逻辑处理,容易留下一大堆 IF ELSE 的臃肿代码。

  • 第三方工具:用BeanUtils工具类 将对应类型的属性一个个copy进去。
  • 现在还可以尝试使用 SpringBoot 的 PropertyMapper 来做数据对象的转换

案例1:SpringBoot 的 RabbitTemplateConfiguration

  • SpringBoot 官方模块 spring-boot-starter-amqpRabbitTemplate 的配置实现
  • 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;
}

案例2:基于 Http11NioProtocol、WebServerFactoryCustomizer 、自定义配置实体,实现 自定义 SpringBoot 的 Tomcat Server 配置

TomcatEmbedServerProperties : 应用程序的自定义配置实体

//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.ComponentScan;
//import org.springframework.context.annotation.Configuration;

/**
 * @create-time 2023/4/11
 * @description ...
 */
//@ComponentScan
//@Configuration
//@ConfigurationProperties(
//    prefix="service-config.tomcat-server"
//    , ignoreUnknownFields = true
//)
public class TomcatEmbedServerProperties { //应用程序的自定义配置实体
    private Integer port;

    private Integer minSpareThreads;

    private Integer maxThreads;

    private Integer acceptCount;

    private Integer maxConnections;

    private Integer maxKeepAliveRequests;

    private Integer keepAliveTimeout;

    private Integer connectionTimeout;

    public TomcatEmbedServerProperties(Integer port, Integer minSpareThreads, Integer maxThreads, Integer acceptCount, Integer maxConnections, Integer maxKeepAliveRequests, Integer keepAliveTimeout, Integer connectionTimeout) {
        this.port = port;
        this.minSpareThreads = minSpareThreads;
        this.maxThreads = maxThreads;
        this.acceptCount = acceptCount;
        this.maxConnections = maxConnections;
        this.maxKeepAliveRequests = maxKeepAliveRequests;
        this.keepAliveTimeout = keepAliveTimeout;
        this.connectionTimeout = connectionTimeout;
    }

    public TomcatEmbedServerProperties() {
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public Integer getMinSpareThreads() {
        return minSpareThreads;
    }

    public void setMinSpareThreads(Integer minSpareThreads) {
        this.minSpareThreads = minSpareThreads;
    }

    public Integer getMaxThreads() {
        return maxThreads;
    }

    public void setMaxThreads(Integer maxThreads) {
        this.maxThreads = maxThreads;
    }

    public Integer getAcceptCount() {
        return acceptCount;
    }

    public void setAcceptCount(Integer acceptCount) {
        this.acceptCount = acceptCount;
    }

    public Integer getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(Integer maxConnections) {
        this.maxConnections = maxConnections;
    }

    public Integer getMaxKeepAliveRequests() {
        return maxKeepAliveRequests;
    }

    public void setMaxKeepAliveRequests(Integer maxKeepAliveRequests) {
        this.maxKeepAliveRequests = maxKeepAliveRequests;
    }

    public Integer getKeepAliveTimeout() {
        return keepAliveTimeout;
    }

    public void setKeepAliveTimeout(Integer keepAliveTimeout) {
        this.keepAliveTimeout = keepAliveTimeout;
    }

    public Integer getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(Integer connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    private static boolean isPositive(int value) {
        return value > 0;
    }

    @Override
    public String toString() {
        return "TomcatEmbedServerProperties{" +
                "port=" + port +
                ", minSpareThreads=" + minSpareThreads +
                ", maxThreads=" + maxThreads +
                ", acceptCount=" + acceptCount +
                ", maxConnections=" + maxConnections +
                ", maxKeepAliveRequests=" + maxKeepAliveRequests +
                ", keepAliveTimeout=" + keepAliveTimeout +
                ", connectionTimeout=" + connectionTimeout +
                '}';
    }
}

WebServerConfiguration : 应用程序的自定义 Tomcat Server 配置 Bean

package xx.xx.biz.common.configuration;

import xx.xx.common.dto.serviceconfig.TomcatEmbedServerProperties;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.TomcatServletWebServerFactoryCustomizer;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.WebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;

/**
 * @create-time 2023/4/3 
 * @description 内嵌tomcat
 *  [1] 在 spring-configuration-metadata.json 文件中相关tomcat配置
 *      server.tomcat.accept-count:                               等待队列长度,默认100
 *      server.tomcat.max-connections:                            最大可连接数,默认10000
 *      server.tomcat.max-threads:                                最大工作线程数,默认200
 *      server.tomcat.min-spare-threads:                          最小工作线程数,默认10
 *      server.tomcat.accesslog.enabled=true                     开启access日志
 *      server.tomcat.accesslog.directory=/var/www//tomcat       日志存放的路径
 *      server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D  日志格式为:请求的host主机地址,时间,方式,路径,协议,状态,返回字节数,处理时间
 *
 *      注意:默认配置下,连接数超过 10000 后出现拒接连接情况;触发的请求超过 200+100 后拒绝
 *   [2] 定制内嵌 tomcat 开发
 *      相关参数:
 *          keepAliveTimeOut :多少毫秒后不响应的断开keepalive
 *          maxKeepAliveRequests :多少次请求后keepalive断开失效
 *      keepAlive 简介:参考这里
 *          优点:
 *              Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。
 *              长连接能够保证服务器和客户端的socket能够高效利用,减少握手等额外的开销。
 *          缺点:
 *              但是对于负担较重的网站来说,这里存在另外一个问题:
 *                  虽然为客户保留打开的连接有一定的好处,但它同样影响了性能,因为在处理暂停期间,本来可以释放的资源仍旧被占用。
 *                  当Web服务器和应用服务器在同一台机器上运行时,Keep-Alive功能对资源利用的影响尤其突出。
 * @reference-doc
 *  [1] springboot内嵌tomcat优化 - 博客园 - https://www.cnblogs.com/hjwucc/p/11425306.html
 *  [2] 我可以为 Spring Boot 的嵌入式 tomcat 启用 tomcat 管理器应用程序吗? - IT1352 - https://www.it1352.com/2405633.html
 *  [3] Tomcat卷三:Jasper引擎 - CSDN - https://blog.csdn.net/m0_53157173/article/details/123131713
 *  [4] Spring Boot 最佳实践(二)集成Jsp与生产环境部署 - 51CTO - https://blog.51cto.com/vipstone/5408719
 *  [5] 嵌入式 Tomcat (Embedded Tomcat) - 博客园 - https://www.cnblogs.com/develon/p/11602969.html
 *
 *  [6] 【Java基础】-- isAssignableFrom的用法详细解析 - 腾讯云 - https://cloud.tencent.com/developer/article/1754376
 *  [7] The HTTP Connector - Apache Tomcat(9) - https://tomcat.apache.org/tomcat-9.0-doc/config/http.html
 *  [8] HTTP/1.1与HTTP/1.0的区别 - CSDN - https://blog.csdn.net/qq_25827845/article/details/80127198
 */

@Configuration
public class WebServerConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(WebServerConfiguration.class);

    // 在某配置类中添加如下内容
    // 监听的http请求的端口,需要在application配置中添加http.port=端口号  如 80
//    @Value("${http.port}")
//    Integer httpPort;

    //正常启用的https端口 如 443
//    @Value("${server.port}")
//    Integer httpsPort;

    @Autowired
    Environment environment;

    @Autowired
    ServerProperties serverProperties;

    @Autowired
    TomcatEmbedServerProperties tomcatServerProperties;//应用程序的 自定义 Tomcat Server 配置实体

//    @Bean
//    public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
//        return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
//    }

    // TomcatServletWebServerFactory 是 ConfigurableWebServerFactory 接口 的一种实现
    @Bean
    //public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer() {
        return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
            @Override
            //public void customize(WebServerFactoryCustomizer factory) {
            public void customize(TomcatServletWebServerFactory factory) {
                /**
                 * step1 关闭扫描 manifest 的 JAR 包
                 * 解决的问题: springboot 项目启动过程中会扫描一些 jar,jar 不存在会提示报错 : "Failed to scan [xxxx.jar] from classloader hierarchy"
                 */
                factory.addContextCustomizers((context) -> {
                    ((StandardJarScanner)context.getJarScanner()).setScanManifest(false);
                });

                //step2 使用工厂类定制 tomcat connector
                TomcatServletWebServerFactory webServerFactory =  ((TomcatServletWebServerFactory)factory);
                //webServerFactory.addContextCustomizers((context -> {
                //    context.addWelcomeFile("/index.html");
                //    // 使用 Tomcat 的 LegacyCookieProcessor 处理器
                //    context.setCookieProcessor(new LegacyCookieProcessor())
                //}));
                //TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer = new TomcatServletWebServerFactoryCustomizer(serverProperties);
                //tomcatServletWebServerFactoryCustomizer.customize(webServerFactory);
                //int order = tomcatServletWebServerFactoryCustomizer.getOrder();

                TomcatConnectorCustomizer tomcatConnectorCustomizer = new TomcatConnectorCustomizer() {
                    @Override
                    public void customize(Connector connector) {
                        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
                        PropertyMapper propertyMapper = PropertyMapper.get();

                        //protocol.setPort(tomcatServerProperties.getPort());
                        propertyMapper.from(tomcatServerProperties::getPort).whenNonNull().to(protocol::setPort);

                        /** Tomcat 9.0.46 默认配置 **/
                        /**
                         * @param 最小备用线程数
                         * @defaultValue 10
                         * tomcat启动时的初始化的线程数
                         */
                        //protocol.setMinSpareThreads(10);
                        propertyMapper.from(tomcatServerProperties::getMinSpareThreads).whenNonNull().to(protocol::setMinSpareThreads);
                        /**
                         * @param 最大线程数
                         * @defaultValue 200
                         * Tomcat可创建的最大的线程数,每一个线程处理一个请求
                         * 超过这个请求数后,客户端请求只能排队,等有线程释放才能处理
                         * 可以在服务器 CPU 核心数的 200~250 倍之间
                         */
                        //protocol.setMaxThreads(200);
                        propertyMapper.from(tomcatServerProperties::getMaxThreads).whenNonNull().to(protocol::setMaxThreads);
                        /**
                         * @param 请求等待队列的容量大小
                         * @defaultValue 100
                         * 当tomcat请求处理线程池中的所有线程都处于忙碌状态时,此时新建的链接将会被放入到pending队列
                         * acceptCount即是此队列的容量,如果队列已满,此后所有的建立链接的请求(accept),都将被拒绝
                         * 在高并发/短链接较多的环境中,可以适当增大此值;当长链接较多的场景中,可以将此值设置为0
                         */
                        //protocol.setAcceptCount(100);
                        propertyMapper.from(tomcatServerProperties::getAcceptCount).whenNonNull().to(protocol::setAcceptCount);
                        /**
                         * @param 在同一时间,tomcat能够接受的最大连接数
                         * @defaultValue 8192
                         * 当达到`max-connections `临界值时,系统可能会基于accept-count继续接受连接
                         * tomcat允许接收和处理的最大链接数,对于BIO而言此值默认与maxThreads参数一样,对于NIO而言此值默认为10000
                         * 此值还受限于系统的 ulimit、CPU、内存等配置。
                         */
                        // protocol.setMaxConnections(8192);
                        propertyMapper.from(tomcatServerProperties::getMaxConnections).whenNonNull().to(protocol::setMaxConnections);
                        /**
                         * @param 处于keepAlive状态的请求的个数
                         * @defaultValue 100
                         * -1 表示不限制,1表示关闭 keepAlive 机制
                         * 建议: 此值为 maxThreads * 0.5; 不得大于 maxThreads
                         */
                        //protocol.setMaxKeepAliveRequests(100);
                        propertyMapper.from(tomcatServerProperties::getMaxKeepAliveRequests).whenNonNull().to(protocol::setMaxKeepAliveRequests);
                        /**
                         * @param Tomcat 在关闭连接(Connection)之前,等待另一个请求的时间 (HTTP 1.1 KeepAlive 持久连接)
                         * @defaultValue 60000
                         * 此值控制/影响: HTTP响应报文中的 2个Header
                         *     Connection: Keep-Alive
                         *     keep-alive: timeout={KeepAliveTimeout/1000}s
                         */
                        //protocol.setKeepAliveTimeout(60000);
                        propertyMapper.from(tomcatServerProperties::getKeepAliveTimeout).whenNonNull().to(protocol::setKeepAliveTimeout);
                        /**
                         * @param 与客户端建立连接后, Tomcat 等待客户端请求的时间
                         * @defaultValue 60000
                         * 如果客户端没有请求进来,等待一段时间后断开连接,释放线程
                         */
                        //protocol.setConnectionTimeout(60000);
                        propertyMapper.from(tomcatServerProperties::getConnectionTimeout).whenNonNull().to(protocol::setConnectionTimeout);
                    }
                };

                webServerFactory.addConnectorCustomizers(tomcatConnectorCustomizer);
                webServerFactory.addContextCustomizers();
            }
        };
    }


    // springboot2 写法
//    @Bean
//    public TomcatServletWebServerFactory tomcatServletContainer() {
//        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
//            @Override
//            protected void postProcessContext(Context context) {
//                SecurityConstraint constraint = new SecurityConstraint();
//                constraint.setUserConstraint("CONFIDENTIAL");
//                SecurityCollection collection = new SecurityCollection();
//                collection.addPattern("/*");
//                constraint.addCollection(collection);
//                context.addConstraint(constraint);
//            }
//        };
//        tomcat.addAdditionalTomcatConnectors(httpConnector());
//        tomcat.addConnectorCustomizers();
//        return tomcat;
//    }

    // springboot 1.x

    /**
     * @reference-doc
     *  [1] Spring Boot如何控制Tomcat缓存? - codenong - https://www.codenong.com/39146476/
     *      EmbeddedServletContainerCustomizer / TomcatEmbeddedServletContainerFactory / ConfigurableEmbeddedServletContainer
     * @return
     */
//当 Spring 容器中没有 TomcatEmbeddedServletContainerFactory 这个 bean 时,会把此 bean 加载进容器
/*    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        return new TomcatEmbeddedServletContainerFactory() {
            @Override
            protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                    Tomcat tomcat) {
                tomcat.addUser("admin", "secret");
                tomcat.addRole("admin", "manager-gui");

                try {
                    tomcat.addWebapp("/manager", "/path/to/manager/app");
                }
                catch (ServletException ex) {
                    throw new IllegalStateException("Failed to add manager app", ex);
                }
                return super.getTomcatEmbeddedServletContainer(tomcat);
            }
        };
    }*/



//    public Connector httpConnector() {
//        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
//        connector.setScheme("http");
//        //Connector监听的http的端口号
//        connector.setPort(httpPort);
//        connector.setSecure(false);
//        //监听到http的端口号后转向到的https的端口号
//        connector.setRedirectPort(httpsPort);
//        return connector;
//    }

//    @Configuration(
//            proxyBeanMethods = false
//    )
//    @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
//    public static class TomcatWebServerFactoryCustomizerConfiguration {
//        public TomcatWebServerFactoryCustomizerConfiguration() {
//        }
//
//        @Bean
//        public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
//            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
//        }
//    }
}

案例3: Order 转 OrderDTO

Order

@Data
public class Order {
    private Long id;

    private BigDecimal totalAmout;

    private Integer status;

    private Long userId;

    private LocalDateTime createTime;
}

OrderDTO

@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 对象中。

3 PropertyMapper API

  • <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 类内部维护一个静态实例,我们一开始只能通过获取它得到 PropertyMapper 实例
get() 方法始终返回的是其内部的 PropertyMapper final static 实例, PropertyMapper 对象本身的内存开销不会太大

  • PropertyMapper alwaysApplying(SourceOperator operator) :自定义过滤规则,参考代码

alwaysApplying 用于向 PropertyMapper 添加一个 when 条件判定,这个判定在每次 from 方法中都被调用。
alwaysApplyingWhenNonNull 则是对这个方法的包装。

  • PropertyMapper alwaysApplyingWhenNonNull() :提供实例时,当前实例就过滤掉from之后是null的元素。PropertyMapper.get().alwaysApplyingWhenNonNull();
//使当前 PropertyMapper 只会映射 LocalDateTime 类型的字段
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);
		}
	} );

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

Y 推荐文献

X 参考文献

posted @ 2024-10-12 10:08  千千寰宇  阅读(30)  评论(0编辑  收藏  举报