Как решить проблему NullPointerException в методе контроллера при работе с параметрами?
Что проверяют
Знаете ли вы, откуда в контроллере берётся null: из отсутствующего параметра, из пустого поля в теле запроса или из сервиса. И умеете ли отсекать его на входе с ответом 400, а не 500. Следом спрашивают про валидацию и глобальную обработку ошибок.
Как отвечать
Сначала найдите место. С Java 15 подробные сообщения NPE (JEP 358) включены по умолчанию: в стектрейсе написано, какое выражение оказалось null, например because the return value of "UserDto.getName()" is null.
Типичные источники в Spring MVC:
@RequestParam: по умолчаниюrequired = true, без параметра Spring сам вернёт 400 черезMissingServletRequestParameterException. NPE появляется, когда поставилиrequired = falseи не проверили. Варианты:defaultValue,Optional<String>в параметре или явная проверка;- примитивы:
int pageсrequired = falseбез параметра падает, потому чтоnullнельзя положить вint. БерутIntegerилиdefaultValue; @RequestBody: поля DTO приходятnull, если клиент их не прислал. Лечится Bean Validation:@NotNull,@NotBlankна полях и@Validна параметре. Ошибка превращается вMethodArgumentNotValidException, то есть в 400 с описанием полей;- автораспаковка:
Integerиз DTO сравнивают через==сintили передают туда, где ждут примитив; - сервис вернул
null: пусть репозиторий отдаётOptional, а контроллер решает, что вернуть, например 404 черезorElseThrow.
Ответы на ошибки собирают в одном месте через @RestControllerAdvice и @ExceptionHandler, а контракт на null помечают аннотацией @Nullable (свежие версии Spring используют аннотации JSpecify).
Где спотыкаются
Ловят NullPointerException в try/catch и пишут в лог. Это прячет ошибку в коде, а клиент всё равно получает невнятный ответ.
Считают, что @NonNull сам защищает. Аннотации JSpecify или JetBrains это подсказка для IDE и анализаторов, в рантайме они ничего не проверяют. Исключение составляет @NonNull из Lombok: он генерирует проверку, которая бросает всё тот же NPE.
Заворачивают в Optional поля DTO и параметры сервисов. Он задуман как тип возвращаемого значения, в полях и сериализации только мешает.