如何解决SpringBoot Kotlin项目使用?仍出现空指针异常的问题
问题根因
- 强制解包运算符
!!滥用:你虽然给DTO字段加了可空标识?,但在代码多处使用!!强行解包可空变量,只要变量为null就会直接抛出空指针,完全失去了空安全的防护效果。 - DTO初始化逻辑风险:
ReqMaterialReturn类中pending:Long? = totalPiecesMaterial- deposit!!这里直接对deposit强制解包,只要deposit为null,DTO实例化阶段就会抛出空指针。 - 参数绑定问题:Controller使用
@ModelAttribute接收表单/查询参数,如果前端未传递withdrawId、userId、deposit这类必填参数,或者传了空值,会直接覆盖你设置的默认值-1,导致变量实际为null。 - 数据库查询结果为空风险:
findByWithdrawId返回值是可空的Long?,如果传入的withdrawId在数据库中没有对应记录,查询结果为null,你用!!强制解包也会触发空指针。
修复方案
1. 修正DTO的安全初始化
把强制解包替换为空安全调用,避免DTO实例化阶段就抛出异常:
data class ReqMaterialReturn( val userId:Long?=-1, val withdrawId:Long?=-1, val deposit:Long?=-1, val totalPiecesMaterial:Long=-1, // 替换deposit!!为安全调用,deposit为null时pending也返回null val pending:Long? = deposit?.let { totalPiecesMaterial - it }, val workStatus:Boolean?=false, val workPrice : Double?= deposit?.times(3.5), val depositedAt: LocalDateTime? = LocalDateTime.now() )
2. 新增必填参数校验,避免空值流入业务逻辑
在Service处理前先校验必填参数是否为空,为空直接抛出业务异常或者返回错误提示,禁止用!!强行解包:
fun acceptMaterial(request:ReqMaterialReturn):MaterialReturn { // 先校验必填参数 val userId = request.userId ?: throw IllegalArgumentException("userId不能为空") val withdrawId = request.withdrawId ?: throw IllegalArgumentException("withdrawId不能为空") val deposit = request.deposit ?: throw IllegalArgumentException("deposit不能为空") // 校验查询结果不为空 val totalPieces = materialReturnRepository.findByWithdrawId(withdrawId) ?: throw IllegalArgumentException("对应withdrawId的领料记录不存在") val depositMaterial = MaterialReturn( userId = userId, withdrawId = withdrawId, deposit = deposit, totalPiecesMaterial = totalPieces ) return materialReturnRepository.save(depositMaterial) }
同时可以把Repository方法的入参改为非空,避免空值传入查询:
@Query("select m.pieces from WithdrawMaterial as m where m.withdrawId =:withdrawId") fun findByWithdrawId(@RequestParam("withdrawId") withdrawId: Long): Long?
3. 优化Controller参数接收
如果你的接口是接收JSON参数,建议把@ModelAttribute换成@RequestBody,参数绑定更稳定,同时可以配合JSR380校验注解做参数自动校验:
首先给DTO加校验注解:
import jakarta.validation.constraints.NotNull data class ReqMaterialReturn( @field:NotNull(message = "userId不能为空") val userId:Long?=-1, @field:NotNull(message = "withdrawId不能为空") val withdrawId:Long?=-1, @field:NotNull(message = "deposit不能为空") val deposit:Long?=-1, // 其余字段保持不变 )
然后Controller修改参数注解,加@Valid开启自动校验:
@PostMapping("/deposit") fun depositMaterial(@RequestBody @Valid request:ReqMaterialReturn):ResponseEntity<*>{ // 原有逻辑保持不变 }
参数为空时框架会自动返回参数错误提示,无需手动编写基础校验逻辑。
内容的提问来源于stack exchange,提问作者Mama
相关产品推荐
相关产品推荐

