HTML Diff
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>