Аннотации Spring: @Component, @Repository, @Service — в чем разница?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему в Spring три аннотации для одного и того же?

Каждый, кто начинал работать со Spring, рано или поздно задавался вопросом: "Зачем нужны три разных аннотации — @Component, @Repository и @Service — если все они просто регистрируют бин в контексте?".

Этот вопрос настолько популярен, что на Stack Overflow он собрал тысячи просмотров и десятки ответов. И это не случайно. На первый взгляд, аннотации действительно делают одно и то же. Но дьявол, как всегда, кроется в деталях.

В этой статье мы не просто перечислим различия. Мы разберем философию Spring, заглянем под капот контейнера и покажем, как правильное использование аннотаций может спасти ваш проект от скрытых багов. Поехали!

Краткий ликбез: Что такое стереотипные аннотации?

Все три аннотации относятся к так называемым стереотипным аннотациям (stereotype annotations). Они сообщают Spring, что класс является управляемым компонентом (бином) и должен быть автоматически обнаружен во время сканирования classpath.

Базовой аннотацией является @Component. Она — фундамент. @Repository, @Service и @Controller — это специализированные формы @Component, которые добавляют дополнительную семантику и поведение.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Вот ключевой момент!
public @interface Service {
    // ...
}

Как видите, @Service сама аннотирована @Component. Это означает, что Spring будет обрабатывать её точно так же, как и обычный @Component. Но зачем тогда всё усложнять?

@Component — универсальный солдат

@Component — это самая общая аннотация. Используйте её, когда ваш класс не подходит ни под одну из более конкретных категорий.

Типичные примеры:

  • Вспомогательные утилиты
  • Конфигурационные объекты (не путать с @Configuration)
  • Любые классы, которые должны быть бинами, но не являются сервисами, репозиториями или контроллерами
import org.springframework.stereotype.Component;

@Component
public class EmailValidator {

    public boolean isValid(String email) {
        return email != null && email.contains("@");
    }
}

Обратите внимание: EmailValidator не выполняет бизнес-логику (это не @Service) и не работает с базой данных (это не @Repository). Поэтому @Component — идеальный выбор.

@Repository — страж данных

@Repository — это первая специализированная аннотация, которая добавляет реальную функциональность. Помимо регистрации бина, она выполняет две важные задачи:

  1. Перехват исключений доступа к данным. Spring автоматически оборачивает исключения от DAO (Data Access Object) в DataAccessException — иерархию исключений Spring.
  2. Улучшение читаемости кода. Любой разработчик, увидев @Repository, сразу понимает: "Ага, здесь работа с базой данных".
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    User findByEmail(String email);
}

Важно: Если вы используете Spring Data JPA и наследуетесь от JpaRepository, то Spring сам создаст прокси-бин. Явная аннотация @Repository не обязательна, но рекомендуется для явного указания назначения.

Главная "фишка" @Repository — это AOP-перехватчик (PersistenceExceptionTranslationPostProcessor). Он переводит специфичные для базы данных исключения (например, SQLException или HibernateException) в универсальные исключения Spring. Без этой аннотации ваши DAO-слои могут "выбрасывать" непереведенные исключения, которые трудно отлавливать.

@Service — сердце бизнес-логики

@Service — это аннотация для сервисного слоя. Она не добавляет никакого дополнительного поведения (как @Repository), но несет мощную семантическую нагрузку.

Используйте @Service для классов, которые содержат бизнес-логику приложения. Это прослойка между контроллерами и репозиториями.

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UserService {

    private final UserRepository userRepository;

    // Конструктор — лучший способ внедрения зависимостей
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User registerUser(String email, String password) {
        // Бизнес-логика: проверка, хеширование пароля, сохранение
        if (userRepository.findByEmail(email) != null) {
            throw new UserAlreadyExistsException("User with email " + email + " already exists");
        }
        User user = new User(email, passwordEncoder.encode(password));
        return userRepository.save(user);
    }
}

Хотя @Service технически ничем не отличается от @Component, его использование — это маркер хорошего тона. В больших проектах это помогает быстро ориентироваться в коде: контроллеры, сервисы, репозитории — всё разложено по полочкам.

Сравнительная таблица: @Component vs @Repository vs @Service

Критерий @Component @Repository @Service
Семантика Общий компонент Слой доступа к данным (DAO) Слой бизнес-логики
Доп. функционал Нет Перехват и перевод исключений БД Нет
Когда использовать Утилиты, хелперы, валидаторы Классы, работающие с БД (JPA, JDBC, MongoDB) Классы с бизнес-логикой
Читаемость кода Минимальная Высокая (понятен слой данных) Высокая (понятен слой логики)
Рекомендация Избегать, если есть более специфичная

Блоги

Рекомендаций по книгам