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

Orika映射不可变Bean的不可修改列表失败,求解决方案示例

我来帮你搞定Orika拷贝不可变列表时抛出异常的问题!先理清楚根源:Orika默认会尝试创建可变集合来承接拷贝的值,但像Collections.unmodifiableList()返回的不可变列表不支持添加/修改操作,所以触发异常。下面给你具体的可运行示例,分两种常用解决方案:

先模拟你的基础类(方便对应你的代码)

Employee 不可变Bean类

import java.util.List;
import java.util.Collections;

public class Employee {
    private final String name;
    private final List<String> skills;

    public Employee(String name, List<String> skills) {
        this.name = name;
        // 初始化时转为不可变列表
        this.skills = Collections.unmodifiableList(skills);
    }

    // 仅提供getter,无setter
    public String getName() { return name; }
    public List<String> getSkills() { return skills; }
}

EmployeeDto 类(通常是可变的)

import java.util.List;

public class EmployeeDto {
    private String name;
    private List<String> skills;

    // 标准getter和setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public List<String> getSkills() { return skills; }
    public void setSkills(List<String> skills) { this.skills = skills; }
}

解决方案1:自定义不可变集合转换器

创建一个全局转换器,让Orika在处理列表时自动转为不可变列表,适配你的Bean需求:

自定义转换器类

import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.metadata.Type;
import java.util.Collections;
import java.util.List;

public class UnmodifiableListConverter extends CustomConverter<List<?>, List<?>> {

    @Override
    public List<?> convert(List<?> source, Type<? extends List<?>> destinationType, MappingContext mappingContext) {
        // 将源列表转为不可变列表返回
        return Collections.unmodifiableList(source);
    }
}

配置Orika并测试

import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import java.util.Arrays;

public class OrikaTest {
    public static void main(String[] args) {
        MapperFactory factory = new DefaultMapperFactory.Builder().build();

        // 注册自定义转换器,全局生效
        factory.getConverterFactory().registerConverter(new UnmodifiableListConverter());

        // 注册类映射规则
        factory.classMap(Employee.class, EmployeeDto.class)
               .byDefault()
               .register();
        factory.classMap(EmployeeDto.class, Employee.class)
               .byDefault()
               .register();

        MapperFacade mapper = factory.getMapperFacade();

        // 测试:Employee -> EmployeeDto
        Employee emp = new Employee("John Doe", Arrays.asList("Java", "Spring"));
        EmployeeDto dto = mapper.map(emp, EmployeeDto.class);
        System.out.println("Dto技能列表: " + dto.getSkills());

        // 测试:EmployeeDto -> Employee(关键:拷贝后仍为不可变列表)
        EmployeeDto dto2 = new EmployeeDto();
        dto2.setName("Jane Smith");
        dto2.setSkills(Arrays.asList("Python", "Django"));
        Employee emp2 = mapper.map(dto2, Employee.class);
        System.out.println("Employee技能列表: " + emp2.getSkills());

        // 验证不可变性
        try {
            emp2.getSkills().add("React");
        } catch (UnsupportedOperationException e) {
            System.out.println("✅ 技能列表确实是不可变的,符合预期");
        }
    }
}

解决方案2:针对不可变Bean的构造器映射

如果你的不可变Bean只有构造器(无setter),可以直接配置Orika使用构造器来创建实例,避免它尝试反射修改字段:

修改Orika配置代码

import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import java.util.Arrays;

public class OrikaTest {
    public static void main(String[] args) {
        MapperFactory factory = new DefaultMapperFactory.Builder().build();

        // 配置Dto转Employee时,直接调用Employee的构造器
        factory.classMap(EmployeeDto.class, Employee.class)
               .constructorA("name", "skills") // 指定构造器的参数名,和Dto的属性对应
               .byDefault()
               .register();

        // Employee转Dto的映射保持默认
        factory.classMap(Employee.class, EmployeeDto.class)
               .byDefault()
               .register();

        MapperFacade mapper = factory.getMapperFacade();

        EmployeeDto dto = new EmployeeDto();
        dto.setName("Jane Smith");
        dto.setSkills(Arrays.asList("Python", "Django"));
        Employee emp = mapper.map(dto, Employee.class);
        
        System.out.println("Employee姓名: " + emp.getName());
        System.out.println("Employee技能列表: " + emp.getSkills());

        // 验证不可变性
        try {
            emp.getSkills().add("Vue");
        } catch (UnsupportedOperationException e) {
            System.out.println("✅ 技能列表不可变");
        }
    }
}

额外提示

  • 如果你的不可变列表是用Guava的ImmutableList、Apache Commons的不可变集合实现的,只需要修改转换器的转换逻辑,比如返回ImmutableList.copyOf(source)即可。
  • 建议使用最新版本的Orika,旧版本对不可变集合的支持可能存在bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:18:21