You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring中如何将WebClient返回的Mono列表赋值给DTO对象属性

问题描述

你定义了包含客户列表属性的PeopleDTO类,代码如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PeopleDTO {

    private String processType;
    private String operation;
    private String entity;
    private String entityType;
    private Long id;
    private Document document;

    @Getter
    @Setter
    class Customer {
        private String systemId;
        private String customerId;
    }
    private List<Customer> customers;
}

你通过WebClient调用其他微服务获取上述客户列表,对应实现方法getCuCoPerson返回值为Mono<CuCoPerson>类型,方法内已完成客户数据的规则过滤,代码如下:

public Mono<CuCoPerson> getCuCoPerson(Integer cucoId, String GS_AUTH_TOKEN) {
        WebClient webClient = WebClient.create();

        return webClient.get()
                .uri(GET_RELATION_BY_ID + cucoId)
                .header("Accept", "application/json")
                .header("Authorization", GS_AUTH_TOKEN)
                .retrieve()
                .bodyToMono(CuCoPerson.class)
                .map(cuCoPerson -> {
                    List<CustomerRelation> matches = cuCoPerson.getRelatedCustomers()
                            .stream()
                            .filter(relation -> relation.getSystemId().equals(400) || relation.getSystemId().equals(300) || relation.getSystemId().equals(410))
                            .filter(relation -> relation.getCustomerId().contains("F"))
                            .collect(Collectors.toList());
                    cuCoPerson.setRelatedCustomers(matches);
                    return cuCoPerson;
                });
    }

该方法返回的cucoPerson数据结构示例如下:

{
    "id": 1,
        "relatedCustomers": [
            {
                "customerId": "xxx",
                "systemId": 999
            }
        ]
}

目前你需要将获取到的客户数据封装到PeopleDTO类实例中,已完成基础属性的硬编码赋值,但不清楚如何处理Mono类型数据完成customers属性的赋值,当前未完成的createPeople方法代码如下:

public PeopleDTO createPeople(Long id) {
        PeopleDTO people = new PeopleDTO();
        people.setProcessType("ONLINE");
        people.setOperation("UPDATE");
        people.setEntity("DOCUMENT");
        people.setEntityType("DOCUMENT");
        people.setIdCuco(id);
        people.setDocument(new Document());
        people.setCustomers(......);
    }

最终期望得到的序列化后JSON结构如下:

{
    "type": "ONLINE",
    "operation": "UPDATE",
    "id": 1,
    "entity": "DOCUMENT",
    "entityType": "NIE",
    "documents": {
        "id": 1,
        "additionals": {
            "issuing_authority": "Spain",
            "country_doc": "ES",
            "place_of_birth": "",
            "valid_from": "1995-08-09",
            "valid_to": "0001-01-01"
        },
        "code": "X12345",
        "typeDocument": "NIE"
    },
    "id": 1,
    "relatedCustomers": [
        {
            "customerId": "xxx",
            "systemId": 999
        }
    ]
}
解决方案

核心问题:Mono是Project Reactor提供的异步响应式包装类型,代表一个未来会返回0或1个结果的异步任务,不能直接同步取值赋值,需要根据项目运行环境选择对应实现方式。

方案1:非阻塞响应式写法(推荐,适配WebFlux环境)

不要返回同步的PeopleDTO,直接将方法返回值改为Mono<PeopleDTO>,在响应式操作符链路内完成属性转换和赋值,全程不阻塞,适配WebClient的响应式编程模型。
首先需要修正PeopleDTO的内部类定义,给Customer加static修饰,否则会出现序列化、实例化异常:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PeopleDTO {

    private String processType;
    private String operation;
    private String entity;
    private String entityType;
    private Long id;
    private Document document;

    @Getter
    @Setter
    public static class Customer { // 必须加static修饰内部类
        private String systemId;
        private String customerId;
    }
    private List<Customer> customers;
}

修正后的createPeople方法实现:

public Mono<PeopleDTO> createPeople(Long id, String GS_AUTH_TOKEN) {
    return getCuCoPerson(id.intValue(), GS_AUTH_TOKEN)
            .map(cuCoPerson -> {
                PeopleDTO people = new PeopleDTO();
                // 基础属性赋值,注意和最终JSON的字段值对齐
                people.setProcessType("ONLINE");
                people.setOperation("UPDATE");
                people.setEntity("DOCUMENT");
                people.setEntityType("NIE");
                people.setId(id);
                // 替换为你自己的Document构造逻辑
                people.setDocument(buildTargetDocument(id));
                // 类型转换:将CuCoPerson的CustomerRelation转为PeopleDTO的Customer
                List<PeopleDTO.Customer> customers = cuCoPerson.getRelatedCustomers()
                        .stream()
                        .map(relation -> {
                            PeopleDTO.Customer customer = new PeopleDTO.Customer();
                            customer.setCustomerId(relation.getCustomerId());
                            // 注意类型转换:CuCoPerson中systemId是数值,PeopleDTO中是字符串
                            customer.setSystemId(String.valueOf(relation.getSystemId()));
                            return customer;
                        }).collect(Collectors.toList());
                people.setCustomers(customers);
                return people;
            });
}

需要额外注意的点:

  • 你定义的DTO字段名和最终期望JSON的字段名存在差异(比如DTO中processType对应JSON的type、DTO中document对应JSON的documents),如果要完全匹配序列化结果,需要在对应字段上加@JsonProperty注解指定序列化名称
  • 如果远程接口返回为空,需要加onErrorResume、defaultIfEmpty等操作符做异常和空值处理,避免NPE

方案2:同步阻塞取值(仅适用于Servlet环境,禁止在WebFlux EventLoop线程中使用)

如果你的项目是传统Spring MVC Servlet环境,必须返回同步的PeopleDTO对象,可以调用block()方法从Mono中阻塞获取结果,必须设置超时时间避免无限等待:

public PeopleDTO createPeople(Long id, String GS_AUTH_TOKEN) {
    PeopleDTO people = new PeopleDTO();
    people.setProcessType("ONLINE");
    people.setOperation("UPDATE");
    people.setEntity("DOCUMENT");
    people.setEntityType("NIE");
    people.setId(id);
    people.setDocument(new Document());
    // 阻塞等待远程接口返回,设置5秒超时
    CuCoPerson cuCoPerson = getCuCoPerson(id.intValue(), GS_AUTH_TOKEN)
            .block(Duration.ofSeconds(5));
    // 判空避免空指针
    if (cuCoPerson != null) {
        List<PeopleDTO.Customer> customers = cuCoPerson.getRelatedCustomers()
                .stream()
                .map(relation -> {
                    PeopleDTO.Customer customer = new PeopleDTO.Customer();
                    customer.setCustomerId(relation.getCustomerId());
                    customer.setSystemId(String.valueOf(relation.getSystemId()));
                    return customer;
                }).collect(Collectors.toList());
        people.setCustomers(customers);
    }
    return people;
}

注意:如果在WebFlux的Netty工作线程中调用block()方法,会抛出BlockingOperationError异常,绝对不要在响应式链路中使用阻塞写法。


内容的提问来源于stack exchange,提问作者DiegoMG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:18:20