0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Spring Boot умеет автоматически преобразовывать объекты в JSON, когда они возвращаются из методов контроллера. Для этого внутри используется библиотека Jackson:</p>
1
<p>Spring Boot умеет автоматически преобразовывать объекты в JSON, когда они возвращаются из методов контроллера. Для этого внутри используется библиотека Jackson:</p>
2
<p>Несмотря на удобство, на практике этот механизм используют редко по нескольким причинам:</p>
2
<p>Несмотря на удобство, на практике этот механизм используют редко по нескольким причинам:</p>
3
<ul><li><strong>Безопасность</strong>: Обычно у пользователя есть свойства, которые не стоит показывать наружу - например, хэш пароля или количество денег на счету. Автоматическое преобразование не учитывает такие данные и возвращает все доступные свойства.</li>
3
<ul><li><strong>Безопасность</strong>: Обычно у пользователя есть свойства, которые не стоит показывать наружу - например, хэш пароля или количество денег на счету. Автоматическое преобразование не учитывает такие данные и возвращает все доступные свойства.</li>
4
<li><strong>Представления</strong>: В разных ситуациях нужно возвращать разные наборы свойств. Для веб-версии нужно что-то одно, а для мобильной - что-то другое. Кроме того, по разным причинам могут отличаться названия свойств.</li>
4
<li><strong>Представления</strong>: В разных ситуациях нужно возвращать разные наборы свойств. Для веб-версии нужно что-то одно, а для мобильной - что-то другое. Кроме того, по разным причинам могут отличаться названия свойств.</li>
5
<li><strong>Схема данных</strong>: Со временем имена полей могут меняться - например, из-за изменений в базе данных. При этом API меняться не должен, потому что на него рассчитывают клиенты. Разделение помогает асинхронно менять названия либо в сущностях, либо в API.</li>
5
<li><strong>Схема данных</strong>: Со временем имена полей могут меняться - например, из-за изменений в базе данных. При этом API меняться не должен, потому что на него рассчитывают клиенты. Разделение помогает асинхронно менять названия либо в сущностях, либо в API.</li>
6
<li><strong>Связи</strong>: Если в сущностях появляются связи с другими сущностями, это может вести к исключениям и другим проблемам во время преобразования в JSON.</li>
6
<li><strong>Связи</strong>: Если в сущностях появляются связи с другими сущностями, это может вести к исключениям и другим проблемам во время преобразования в JSON.</li>
7
</ul><p>В Jackson встроена аннотация @JsonIgnore, которая в простых случаях помогает решить проблемы с безопасностью. Если пометить этой аннотацией какое-то поле сущности, оно будет проигнорировано при конвертации в JSON:</p>
7
</ul><p>В Jackson встроена аннотация @JsonIgnore, которая в простых случаях помогает решить проблемы с безопасностью. Если пометить этой аннотацией какое-то поле сущности, оно будет проигнорировано при конвертации в JSON:</p>
8
<p>У этого механизма есть две проблемы:</p>
8
<p>У этого механизма есть две проблемы:</p>
9
<ul><li>Это антипаттерн, который нарушает саму суть MVC. Модель узнает, как она используется в слое представления.</li>
9
<ul><li>Это антипаттерн, который нарушает саму суть MVC. Модель узнает, как она используется в слое представления.</li>
10
<li>Этот механизм не решает проблемы, описанные выше. В разных ситуациях мы работаем с разными наборами полей с точки зрения безопасности и представлений. Аннотация @JsonIgnore работает, только когда существует единственное представление - в реальных проектах такое встречается редко.</li>
10
<li>Этот механизм не решает проблемы, описанные выше. В разных ситуациях мы работаем с разными наборами полей с точки зрения безопасности и представлений. Аннотация @JsonIgnore работает, только когда существует единственное представление - в реальных проектах такое встречается редко.</li>
11
</ul><p>Для решения этих задач был придуман шаблон проектирования<strong>Data Transfer Object</strong>(<em>DTO</em>). По этому паттерну мы должны создавать свой класс с особыми набором полей под каждую конкретную ситуацию, которая требует своего набора полей. Затем необходимые данные из модели нужно копировать в DTO и возвращать наружу.</p>
11
</ul><p>Для решения этих задач был придуман шаблон проектирования<strong>Data Transfer Object</strong>(<em>DTO</em>). По этому паттерну мы должны создавать свой класс с особыми набором полей под каждую конкретную ситуацию, которая требует своего набора полей. Затем необходимые данные из модели нужно копировать в DTO и возвращать наружу.</p>
12
<p>Для примера выше нам понадобится класс UserDTO:</p>
12
<p>Для примера выше нам понадобится класс UserDTO:</p>
13
<p>DTO - это не часть Spring Boot, поэтому именование и расположение этих классов лежит полностью на программистах. Мы будем хранить эти классы в директории<em>src/main/java/dto</em>.</p>
13
<p>DTO - это не часть Spring Boot, поэтому именование и расположение этих классов лежит полностью на программистах. Мы будем хранить эти классы в директории<em>src/main/java/dto</em>.</p>
14
<p>Когда класс написан, остается только внедрить его в контроллер:</p>
14
<p>Когда класс написан, остается только внедрить его в контроллер:</p>
15
<p>Таким же образом мы поступим и во всех остальных ситуациях. Например, со списками:</p>
15
<p>Таким же образом мы поступим и во всех остальных ситуациях. Например, со списками:</p>
16
<p>Для удобства мы вынесли преобразование сущности в DTO в отдельный приватный метод. Это помогает немного снизить уровень дублирования, но не освобождает от ручного копирования свойств из одного объекта в другой. В следующих уроках мы познакомимся с библиотекой для автоматического копирования свойств.</p>
16
<p>Для удобства мы вынесли преобразование сущности в DTO в отдельный приватный метод. Это помогает немного снизить уровень дублирования, но не освобождает от ручного копирования свойств из одного объекта в другой. В следующих уроках мы познакомимся с библиотекой для автоматического копирования свойств.</p>
17
<p>В наших примерах для списка и вывода конкретной сущности мы использовали один класс UserDTO, но это не обязательно. Если набор полей будет разным, то на каждый набор понадобится свой собственный класс: UserDTO, CreateUserDTO, UserListDTO, AdminUserListDTO и так далее.</p>
17
<p>В наших примерах для списка и вывода конкретной сущности мы использовали один класс UserDTO, но это не обязательно. Если набор полей будет разным, то на каждый набор понадобится свой собственный класс: UserDTO, CreateUserDTO, UserListDTO, AdminUserListDTO и так далее.</p>