JavaRush /Курсы /Модуль 5. Spring /Лекция 155: Настройка контроллеров и сервисов для обработ...

Лекция 155: Настройка контроллеров и сервисов для обработки запросов

Модуль 5. Spring
16 уровень , 4 лекция
Открыта

Изучение Spring MVC напоминает процесс очеловечивания робота: вы добавляете уши (контроллеры для принятия запросов), мозг (сервисы для обработки логики) и руки (репозитории для работы с базой данных). Сегодня мы сосредоточимся на ушах и мозге. Репозитории уже были созданы ранее, так что стоит доверить им непосредственное взаимодействие с базой данных.


Что такое контроллеры и сервисы в Spring MVC?

Контроллеры получают входящие запросы и решают, как их обработать, передавая задачи сервисам. Сервисы, в свою очередь, реализуют бизнес-логику, а если потребуется, обращаются к репозиториям (аналог склада с ингредиентами).

Controller:

  • Отвечает за маршрутизацию запросов на правильный обработчик.
  • Обрабатывает входные данные (например, JSON из HTTP-запроса).
  • Возвращает ответ клиенту.

Service:

  • Выполняет основную бизнес-логику.
  • Декларирует и/или использует методы репозитория для работы с базой данных.
  • Гарантирует, что функциональность является изолированной и переиспользуемой.

Создание REST-контроллеров: обработка входящих HTTP-запросов

REST-контроллеры — это сердце вашего веб-приложения. Они принимают HTTP-запросы (GET, POST, PUT, DELETE) и возвращают ответы. Для их создания используется аннотация @RestController, а эндпоинты задаются с помощью аннотаций @RequestMapping, @GetMapping, @PostMapping и т.д.

Пример минимального контроллера:


@RestController // Говорит Spring, что этот класс будет контроллером
@RequestMapping("/api/v1/users") // Определяет базовый путь для всех эндпоинтов внутри контроллера
public class UserController {

    @GetMapping("/{id}") // Обработка GET-запросов по пути /api/v1/users/{id}
    public String getUserById(@PathVariable Long id) {
        return "User with ID: " + id; // Возвращает строку с ID пользователя
    }

    @PostMapping // Обработка POST-запросов на /api/v1/users
    public String createUser(@RequestBody String userName) {
        return "Created user: " + userName; // Возвращает сообщение
    }
}
  • @RestController: указывает, что возвращаемые данные будут в формате JSON или XML.
  • @RequestMapping: устанавливает базовый URL для всех обработчиков в этом контроллере.
  • @GetMapping, @PostMapping и т.д.: настраивают обработку определённых HTTP-методов (GET, POST, DELETE, PUT).
  • @PathVariable: позволяет извлекать параметры из части URL.
  • @RequestBody: читает тело запроса, преобразуя его из JSON в объект Java.

Разработка сервисного слоя для бизнес-логики

Как мы выяснили, контроллеры не должны содержать бизнес-логику — это зона ответственности сервисов. Они выступают мостом между контроллерами и репозиториями. Сервисы создаются как компоненты Spring с использованием аннотации @Service.

Пример сервиса:


@Service // Помечает класс как Spring-компонент
public class UserService {

    private final UserRepository userRepository; // Репозиторий для взаимодействия с базой данных

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        return userRepository.findById(id) // Получаем пользователя из базы
                .orElseThrow(() -> new RuntimeException("User not found!")); // Ошибка, если пользователь не найден
    }

    public void createUser(User user) {
        userRepository.save(user); // Сохраняем пользователя в базу данных
    }
}
  • @Service: сообщает Spring, что это компонент, содержащий бизнес-логику.
  • UserRepository: автоматически внедряется через конструктор (Dependency Injection, DI).
  • findById и save: стандартные методы репозитория для CRUD-операций.

Интеграция контроллеров с сервисами

Контроллеры и сервисы работают в связке. Контроллер вызывает методы сервиса для обработки запросов. Для этого мы передаём сервис как зависимость в контроллер через DI.

Пример интеграции:


@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService; // Внедрение зависимости
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id); // Вызов метода сервиса
    }

    @PostMapping
    public void createUser(@RequestBody User user) {
        userService.createUser(user); // Вызов метода сервиса
    }
}

Улучшение структуры проекта через использование паттернов

Чтобы ваш проект был понятным и поддерживаемым, стоит следовать таким паттернам:

  • Controller-Service-Repository: никогда не смешивайте логику из контроллеров и сервисов.
  • DTO (Data Transfer Object): используйте DTO для передачи данных между слоями. Это особенно важно, когда структура сущности базы данных и данных, ожидаемых клиентом, различается.
  • Распределение обязанностей: контроллеры занимаются только запросами, сервисы реализуют логику, а репозитории работают с базой данных.

Пошаговый пример разработки

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

1. Сущность:


@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    // Геттеры и сеттеры...
}

2. Репозиторий:


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Возможны дополнительные кастомные запросы
}

3. Сервис:


@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public void saveUser(User user) {
        userRepository.save(user);
    }
}

4. Контроллер:


@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        return ResponseEntity.ok(userService.getAllUsers());
    }

    @PostMapping
    public ResponseEntity<String> createUser(@RequestBody User user) {
        userService.saveUser(user);
        return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
    }
}

Типичные ошибки и способы их устранения

  1. Смешивание логики в контроллере: не пишите бизнес-логику в контроллере. Сервисы лучше для этого подходят.
  2. Отсутствие обработки ошибок: убедитесь, что вы обрабатываете исключения (например, через @ExceptionHandler или @ControllerAdvice).
  3. Невалидация данных: перед использованием данных запросов примените валидацию через аннотацию @Valid.
  4. Циклические зависимости: используйте DI корректно, избегая взаимных ссылок между компонентами.

Теперь ваши контроллеры и сервисы готовы к обработке запросов, а мозг и уши вашего приложения отлично сработаны. В следующей лекции мы продолжим стройку нашего фулл-стека, погружаясь в вопросы безопасности и аутентификации через Spring Security!

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ