ModelMapper自定义Converter映射paymentStatus时空指针异常排查
ModelMapper映射Purchase实体触发空指针异常根因与修复
核心报错原因
异常触发的直接原因是自定义Converter执行时未做非空防护,映射链路中传入的source对象为null时,直接强转后调用Purchase.getPurchaseTransactions()方法触发空指针。
典型错误写法
- 自定义
paymentStatus字段转换逻辑的Converter实现中,未对上下文返回的source对象做null判断,直接执行类型强转和关联集合方法调用 - ModelMapper全局配置未开启空值跳过规则,嵌套映射、级联映射过程中遇到空属性时,仍会强制触发对应属性的Converter执行
- 业务层
toDto转换方法未做入参校验,存在直接将null对象传入modelMapper.map()方法的场景 - 额外触发场景:Purchase类的
purchaseTransactions一对多关联配置为懒加载时,持久化会话关闭后未初始化集合,调用get方法也会抛出类空指针的代理异常
修复步骤
- 给自定义Converter增加前置非空校验
// 自定义支付状态转换逻辑 Converter<Purchase, Integer> paymentStatusConverter = context -> { Purchase source = context.getSource(); // 空值直接返回默认值,避免NPE if (source == null || source.getPurchaseTransactions() == null) { // 可根据业务规则返回默认状态,例如0=未发起支付 return 0; } List<Transaction> transactions = source.getPurchaseTransactions(); // 保留原有支付状态计算逻辑 // 示例逻辑:全量已支付返回2,部分支付返回1,无交易返回0 long paidCount = transactions.stream() .filter(t -> TransactionStatus.PAID.equals(t.getStatus())) .count(); if (paidCount == 0) return 0; if (paidCount == transactions.size()) return 2; return 1; };
- 调整ModelMapper全局配置,开启空值跳过
@Configuration public class ModelMapperConfig { @Bean public ModelMapper modelMapper() { ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration() .setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE) .setFieldMatchingEnabled(true) .setSkipNullEnabled(true) // 核心配置:映射时跳过null属性,不触发对应转换器 .setMatchingStrategy(MatchingStrategies.STRICT); // 注册自定义转换器 modelMapper.addConverter(paymentStatusConverter()); modelMapper.typeMap(Purchase.class, PurchaseDTO.class) .addMappings(mapper -> mapper.using(paymentStatusConverter()) .map(source -> source, PurchaseDTO::setPaymentStatus)); return modelMapper; } @Bean public Converter<Purchase, Integer> paymentStatusConverter() { // 此处放上面写的带非空校验的converter逻辑 } }
- 业务转换入口增加非空拦截
@Service public class PurchaseService { @Resource private ModelMapper modelMapper; public PurchaseDTO convertToDto(Purchase entity) { // 入口处拦截null入参,避免无效映射执行 if (entity == null) { return null; } return modelMapper.map(entity, PurchaseDTO.class); } }
单元测试校验点
- 构造测试用例时,无交易记录的Purchase实例要将
purchaseTransactions设置为空集合而非null - 覆盖入参为null、关联集合为null的边界场景,确认不会抛出转换异常
内容的提问来源于stack exchange,提问作者Roman_G
相关产品推荐
相关产品推荐

