В чому різниця між @Component, @Service, @Repository, @Controller?
Усі ці анотації створюють біни, але для різних шарів додатку:
🟢 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
- @Component → базовий, використовуйте рідко
- @Service → бізнес-логіка
- @Repository → тільки для DAO
- @RestController → REST API
- AOP → точні pointcuts за стереотипами
- Уникайте @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. Що робити, якщо є кілька бінів одного типу]]