Spring Boot
2026-02-26 20:04 Diff

Spring Boot умеет автоматически преобразовывать объекты в JSON, когда они возвращаются из методов контроллера. Для этого внутри используется библиотека Jackson:

Несмотря на удобство, на практике этот механизм используют редко по нескольким причинам:

  • Безопасность: Обычно у пользователя есть свойства, которые не стоит показывать наружу — например, хэш пароля или количество денег на счету. Автоматическое преобразование не учитывает такие данные и возвращает все доступные свойства.
  • Представления: В разных ситуациях нужно возвращать разные наборы свойств. Для веб-версии нужно что-то одно, а для мобильной — что-то другое. Кроме того, по разным причинам могут отличаться названия свойств.
  • Схема данных: Со временем имена полей могут меняться — например, из-за изменений в базе данных. При этом API меняться не должен, потому что на него рассчитывают клиенты. Разделение помогает асинхронно менять названия либо в сущностях, либо в API.
  • Связи: Если в сущностях появляются связи с другими сущностями, это может вести к исключениям и другим проблемам во время преобразования в JSON.

В Jackson встроена аннотация @JsonIgnore, которая в простых случаях помогает решить проблемы с безопасностью. Если пометить этой аннотацией какое-то поле сущности, оно будет проигнорировано при конвертации в JSON:

У этого механизма есть две проблемы:

  • Это антипаттерн, который нарушает саму суть MVC. Модель узнает, как она используется в слое представления.
  • Этот механизм не решает проблемы, описанные выше. В разных ситуациях мы работаем с разными наборами полей с точки зрения безопасности и представлений. Аннотация @JsonIgnore работает, только когда существует единственное представление — в реальных проектах такое встречается редко.

Для решения этих задач был придуман шаблон проектирования Data Transfer Object (DTO). По этому паттерну мы должны создавать свой класс с особыми набором полей под каждую конкретную ситуацию, которая требует своего набора полей. Затем необходимые данные из модели нужно копировать в DTO и возвращать наружу.

Для примера выше нам понадобится класс UserDTO:

DTO — это не часть Spring Boot, поэтому именование и расположение этих классов лежит полностью на программистах. Мы будем хранить эти классы в директории src/main/java/dto.

Когда класс написан, остается только внедрить его в контроллер:

Таким же образом мы поступим и во всех остальных ситуациях. Например, со списками:

Для удобства мы вынесли преобразование сущности в DTO в отдельный приватный метод. Это помогает немного снизить уровень дублирования, но не освобождает от ручного копирования свойств из одного объекта в другой. В следующих уроках мы познакомимся с библиотекой для автоматического копирования свойств.

В наших примерах для списка и вывода конкретной сущности мы использовали один класс UserDTO, но это не обязательно. Если набор полей будет разным, то на каждый набор понадобится свой собственный класс: UserDTO, CreateUserDTO, UserListDTO, AdminUserListDTO и так далее.