Питання 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. Що робити, якщо є кілька бінів одного типу]]