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
相关产品推荐
相关产品推荐

