Service

  • Рубрика записи:Spring back-end

Сервисы (service) – это классы в проекте Spring, которые отвечают за управление объектами сущностей. Сервис – это бизнес-логика. В совокупности, сервисы представляют собой прослойку между контроллерами и репозиториями, обеспечивая выполнение правил и операций над данными.

Напишем парочку сервисов!

Создание сервиса

Для создания сервиса вам нужно просто создать класс и пометить его аннотацией @Service. Как правило, название сервиса складывают из названия класса сущности и слова Service. Например:

Цепочка такая. Репозиторий оперирует информацией напрямую из БД, сервис эксплуатирует репозиторий, а контроллер эксплуатирует сервис:

Схема цепочки действий в Spring-проекте с БД, репозиторием, сервисом и контроллером

При этом контроллер никак не взаимодействует, например, с репозиторием. И, в свою очередь, сервис не владеет непосредственным доступом к БД. Это позволяет более-менее абстрагироваться от конкретной реализации каждого из них. Они взаимодействуют через слои. Каждый слой может быть изменён, но взаимодействие останется таким, каким и было. Такова структура Spring-проекта.

Для того, чтобы использовать репозиторий в сервисе, нужно поместить в сервис переменную репозитория. Но управлять тем, чтобы создать репозиторий и поместить в эту переменную, мы с вами не будем. Этим займётся Spring. Для того, чтобы он знал, что делать, есть несколько способов:

Самый простой способ – пометить нужное поле аннотацией @Autowired. Spring сам разберётся с тем, чтобы подставить в это поле объект репозитория.

Следующий способ – создать конструктор, принимающий аргументом поле, которое нужно заполнить. При этом аннотация @Autowired необязательна, так как Spring автоматически ищет подходящий компонент для внедрения. Но лучше всё-таки обозначать её для большей явности.

Ещё вы можете создать сеттер для репозитория и пометить его аннотацией @Autowired. Spring также автоматически внедрит зависимость.

Теперь мы готовы к тому, чтобы написать методы сервиса с использованием репозитория.

Простой сервис

В небольших и относительно несложных проектах слой сервисов зачастую просто повторяет функциональность слоя репозиториев. Это происходит потому, что сервис просто делегирует запросы к репозиторию без какой-либо дополнительной бизнес-логики. Но даже в маленьких проектах так случается не всегда и не во всём. А в проектах побольше – очень редко.

Простой сервис по управлению статьями:

Как видно, ArticleService просто является оболочкой для той же самой функциональности из ArticleRepository. Но чаще сервисы всё-таки привносят что-то от себя. Давайте узнаем, каким образом.

Усложнённые сервисы

Посмотрим, как сервисы могут предоставлять дополнительную бизнес-логику, не копируя функционал репозитория.

Использование данных из нескольких репозиториев

Например, сервис может содержать методы, которые используют данные из нескольких репозиториев. Можно даже указать, чтобы методы сервиса проходили в рамках одной транзакции. Либо все операции должны быть успешно завершены, либо ни одна из них не должна быть применена.

Когда метод или класс помечается аннотацией @Transactional, Spring автоматически управляет транзакциями вокруг этих методов или всего класса. Если метод вызывает другие методы, помеченные аннотацией @Transactional, то Spring создаёт вложенные транзакции, которые могут быть приняты или откатаны независимо друг от друга.

Метод этого сервиса saveUserAndOrder() либо сохранит и пользователя, и заказ, либо не сохранит ничего – если что-то пойдёт не так, транзакция будет отменена целиком.

Фильтрация и сортировка данных

Сервис может содержать методы, которые фильтруют и сортируют данные, полученные из репозитория, в соответствии с определёнными правилами. Например, сортировка продуктов по цене по возрастанию:

Вычисления и операции

Сервис может содержать методы, которые выполняют сложные вычисления или анализ данных, полученных из репозитория. Например, расчёт средней цены продуктов:

Обработка исключений

Сервис может содержать методы для обработки определённых исключений, возникающих во время выполнения операций с данными.

Пример с User и Post

Продолжим наш пример с созданными ранее двумя таблицами в БД: users и posts. Мы создали для них классы-сущности, а также репозитории. Время создать сервисы.

Сервисы будут простыми. Они будут практически копировать функциональность репозиториев. Обойдёмся без усложнений на первых порах. Я буду использовать библиотеку Lombok для автоматического создания конструктора со всеми final-полями с помощью аннотации @RequiredArgsConstructor. Spring увидит этот единственный конструктор и самостоятельно положит туда репозитории.

Сервис для сущности User:

Сервис для сущности Post:

Итого, все базовые операции CRUD реализованы, и даже чуть больше. Для пользователей, к примеру – дополнительно поиск по email-у. Для записей – поиск по id пользователя, который эти записи создавал. Что логично.

Так как метод findById() у репозиториев возвращает не сам объект сущности, а Optional, то приходится с ним соответствующим образом обращаться. Обёртка Optional означает, что объект может там лежать, а может и не лежать. Поэтому используем orElse(null), чтобы в случае отсутствия объекта возвращало null. Обойдёмся без исключений.

Вот мы и разобрались с очередным слоем Spring-приложения. Отличная работа!

Добавить комментарий