Вопрос 25 · Раздел 5

В чём разница между @Component, @Service, @Repository, @Controller?

Все эти аннотации создают бины, но для разных слоёв приложения:

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Все эти аннотации создают бины, но для разных слоёв приложения:

Аннотация Для чего Особенность
@Component Общий бин Базовая аннотация
@Service Бизнес-логика Семантическая метка
@Repository Работа с БД Автоматическая обработка исключений
@Controller Web MVC Возвращает View (HTML)
@RestController REST API Возвращает JSON
@Repository
public class UserRepository { }

@Service
public class UserService { }

@RestController
public class UserController { }

🟡 Middle Level

@Repository — трансляция исключений

Трансляцию выполняет НЕ сама аннотация @Repository, а PersistenceExceptionTranslationPostProcessor.
Он создаёт прокси вокруг бина и перехватывает исключения. Без подключённого
JPA/Hibernate трансляции НЕ будет  исключения пройдут как есть.

@RestController

// @RestController = @Controller + @ResponseBody. @ResponseBody говорит Spring: не ищи
// View (HTML-шаблон), а сериализуй возвращаемый объект в HTTP-ответ (обычно JSON через Jackson).

// @Controller → возвращает View (HTML)
@Controller
public class PageController {
    @GetMapping("/page")
    public String page() { return "page.html"; }  // View
}

// @RestController → возвращает JSON
@RestController
public class ApiController {
    @GetMapping("/api/user")
    public User user() { return new User(); }  // JSON
}

AOP Pointcuts

// Применить аспект только к сервисам:
@Before("within(@org.springframework.stereotype.Service *)")
public void logServiceCall() { }

🔴 Senior Level

PersistenceExceptionTranslationPostProcessor

@Repository → прокси → перехватывает исключения
  → PersistenceExceptionTranslator
    → JpaDialect, HibernateExceptionTranslator
      → Преобразует в DataAccessException

Component Scanning оптимизация

spring-context-indexer:
  → META-INF/spring.components при компиляции
  → Spring читает список вместо сканирования
  → +50% к скорости старта

Proxy Overhead

@Repository создаёт прокси для трансляции исключений.
Оверхед измеряется наносекундами — для большинства приложений пренебрежимо мало.
Имеет значение при >100K вызовов/сек.

Production Experience

Реальный сценарий: Repository не транслирует

@Repository на классе
  → Self-invocation вызов метода
  → Прокси не сработал
  → SQL Exception вместо DataAccessException

Решение: вынести в отдель бин

Best Practices

  1. @Component → базовый, используйте редко
  2. @Service → бизнес-логика
  3. @Repository → только для DAO
  4. @RestController → REST API
  5. AOP → точные pointcuts по стереотипам
  6. Избегайте @Component для DAO/Controller

Резюме для Senior

  • @Repository → трансляция исключений через прокси
  • @Service → семантика, AOP pointcuts
  • @RestController → JSON, не View
  • Proxy overhead → 1-5 мкс на вызов
  • Self-invocation → прокси не работает
  • spring-context-indexer → ускорение сканирования

🎯 Шпаргалка для интервью

Обязательно знать:

  • Все 4 аннотации — стереотипы @Component, но для разных слоёв архитектуры
  • @Repository: PersistenceExceptionTranslationPostProcessor транслирует SQLException → DataAccessException
  • @Service: семантическая метка + точка для AOP (within(@Service *))
  • @Controller: возвращает View (HTML-шаблон), @RestController = @Controller + @ResponseBody → JSON
  • Трансляция исключений работает только с подключённым JPA/Hibernate и через прокси
  • Self-invocation (вызов своего метода внутри бина) не проходит через прокси — исключения не транслируются
  • Proxy overhead: 1-5 мкс на вызов, заметно при >100K вызовов/сек

Частые уточняющие вопросы:

  • Почему @Repository транслирует исключения, а @Service — нет? — PersistenceExceptionTranslationPostProcessor создаёт прокси только вокруг @Repository.
  • Что будет при self-invocation в @Repository? — Прокси не сработает, SQLException не будет транслирован.
  • В чём разница @Controller и @RestController? — @Controller возвращает имя View, @RestController сериализует объект в JSON (@ResponseBody).
  • Зачем нужны разные аннотации, если все они = @Component? — Семантика, AOP pointcuts, автоматическая обработка (исключения, веб).

Красные флаги (НЕ говорить):

  • «@Service тоже транслирует исключения» — нет, только @Repository.
  • «@RestController — это отдельная аннотация, не связанная с @Controller» — нет, это @Controller + @ResponseBody.
  • «Прокси @Repository работает при self-invocation» — нет, Spring AOP не перехватывает внутренние вызовы.
  • «@Component подходит для DAO» — нет, используйте @Repository для трансляции исключений.

Связанные темы:

  • [[23. Что делает аннотация @ComponentScan]]
  • [[26. Что делает аннотация @Autowired]]
  • [[24. Что такое @Configuration класс]]
  • [[27. Что делать, если есть несколько бинов одного типа]]