В чому різниця між @Component, @Service, @Repository, @Controller
Усі чотири анотації (@Component, @Service, @Repository, @Controller) є стереотипними анотаціями Spring (Stereotype Annotations). Будь-який клас, позначений однією з них, автомат...
🟢 Junior Level
Усі чотири анотації (@Component, @Service, @Repository, @Controller) є стереотипними анотаціями Spring (Stereotype Annotations). Будь-який клас, позначений однією з них, автоматично виявляється процесом @ComponentScan та реєструється як бін в ApplicationContext.
Анотація @Component є батьківською (базовою мета-анотацією), а @Service, @Repository та @Controller — її спеціалізованими спадкоємцями для шаруватої архітектури додатка.
Зведена таблиця стереотипів
| Анотація | Архітектурний шар | Призначення | Особлива поведінка в рантаймі |
|---|---|---|---|
@Component |
Будь-який шар | Базовий компонент загального призначення (утиліти, хелпери, клієнти) | Немає (стандартний бін) |
@Service |
Сервісний шар | Бізнес-логіка додатка, оркестрація операцій | Немає (семантичний маркер) |
@Repository |
Шар даних (DAO) | Доступ до даних та виконання запитів до БД | Так: автоматична трансляція винятків БД в ієрархію DataAccessException |
@Controller |
Шар представлення | Обробка HTTP-запитів та повернення HTML-сторінок (View) | Так: зв’язування з DispatcherServlet та повернення шаблонів View |
@RestController |
REST API | Обробка REST-запитів та повернення JSON/XML (@Controller + @ResponseBody) |
Так: автоматична серіалізація відповідей через HttpMessageConverter |
🟡 Middle Level
Ієрархія мета-анотацій
У вихідному коді Spring анотації @Service, @Repository та @Controller самі позначені анотацією @Component:
// Вихідний код Spring Framework:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // <-- Мета-анотація!
public @interface Service { ... }
Завдяки механізму мета-анотацій сканер @ComponentScan бачить будь-який @Service як @Component.
@Controller vs @RestController
@Controllerпризначений для класичного Spring MVC. Рядок, що повертається методом, інтерпретується механізмомViewResolverяк ім’я HTML-шаблону (Thymeleaf, FreeMarker):@Controller public class WebPageController { @GetMapping("/home") public String homePage() { return "home"; // Відрендерить файл resources/templates/home.html } }@RestController— це мета-анотація, що об’єднує@Controllerта@ResponseBody. Вона вказує фреймворку, що значення, яке повертає метод, потрібно не відправляти у ViewResolver, а напряму серіалізувати в тіло HTTP-відповіді (за замовчуванням у формат JSON за допомогою бібліотеки Jackson):@RestController // = @Controller + @ResponseBody public class UserApiController { @GetMapping("/api/users/{id}") public UserDto getUser(@PathVariable Long id) { return new UserDto(id, "Alex"); // Серіалізується в {"id": 1, "name": "Alex"} } }
🔴 Senior Level
Головна функціональна відмінність: Трансляція винятків у @Repository
Єдина анотація серед стереотипів, яка додає реальну функціональну поведінку в рантаймі — це @Repository.
Навіщо потрібна трансляція винятків:
Різні СУБД та бібліотеки доступу до даних викидають специфічні низькорівневі винятки:
- JDBC викидає перевіряємий
java.sql.SQLException. - Hibernate викидає
org.hibernate.HibernateException. - JPA викидає
jakarta.persistence.PersistenceException.
Якби сервісний шар обробляв ці винятки напряму, бізнес-логіка виявилася б намертво прив’язаною до конкретної реалізації БД.
Механізм PersistenceExceptionTranslationPostProcessor:
- Під час старту контексту Spring автоматично реєструє
PersistenceExceptionTranslationPostProcessor(який єBeanPostProcessor). - Цей процесор шукає біни, позначені анотацією
@Repository. - Навколо кожного
@Repositoryстворюється AOP-проксі з порадникомPersistenceExceptionTranslationAdvisor. - Будь-які низькорівневі помилки вендора перехоплюються та конвертуються в єдину неперевіряєму (
unchecked) ієрархію Spring —DataAccessException:
java.sql.SQLException / HibernateException
│
▼ (PersistenceExceptionTranslator)
DataAccessException (Spring Unchecked)
├── DuplicateKeyException (Порушення UNIQUE)
├── DataIntegrityViolationException (Порушення зовнішнього ключа FK / NOT NULL)
├── CannotAcquireLockException (Deadlock блокувань БД)
└── EmptyResultDataAccessException
Важливо: Якщо замінити анотацію
@Repositoryна@Componentабо@Service, клас продовжить працювати як бін, але автоматична трансляція винятків вимкнеться. Сервісному шару доведеться перехоплювати «сирі» винятки драйвера БД!
Використання стереотипів у зрізах AspectJ (Pointcuts)
Використання семантично коректних анотацій дозволяє будувати елегантні правила для наскрізних аспектів (AOP):
@Aspect
@Component
public class ArchitectureMonitoringAspect {
// Застосувати логування тільки до сервісного шару:
@Around("@within(org.springframework.stereotype.Service)")
public Object profileServices(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
// Заміряти метрики продуктивності запитів до БД:
@Around("@within(org.springframework.stereotype.Repository)")
public Object profileDatabaseCalls(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
}
🎯 Шпаргалка для інтерв’ю
30-секундна відповідь
Усі 4 анотації слугують для автоматичної реєстрації класів як бінів Spring через
@ComponentScan:
@Component— базовий стереотип для компонентів загального призначення.@Service— семантична мітка для бізнес-логіки; у рантаймі ідентична@Component, але слугує якорем для транзакцій та аспектів.@Repository— шар роботи з даними. Єдина анотація, що додає поведінку в рантаймі: активує AOP-проксі черезPersistenceExceptionTranslationPostProcessor, який перетворює винятки вендорів (SQLException,HibernateException) в уніфіковану неперевіряєму ієрархіюDataAccessException.@Controller— обробник Spring MVC, що повертає View.@RestController— композиція@Controller+@ResponseBody, що повертає серіалізовані дані (JSON/XML).
4 каверзних питання з відповідями
-
Яка зі стереотипних анотацій має реальну поведінку в рантаймі, а не лише семантичне значення? Відповідь: Анотація
@Repository. За її наявності Spring огортає бін у проксі за допомогоюPersistenceExceptionTranslationPostProcessorдля автоматичної трансляції низькорівневих винятків баз даних в ієрархіюDataAccessException. -
Що станеться, якщо замінити
@Serviceна@Componentнад класом сервісу? Відповідь: З точки зору створення та впровадження біна нічого не зміниться: Spring точно так само зареєструє його в контексті. Проте погіршиться читабельність архітектури, і перестануть спрацьовувати AOP-аспекти, прив’язані до предикату@within(org.springframework.stereotype.Service). -
У чому різниця між
@Controllerта@RestController? Відповідь: Метод@Controllerповертає ім’я View (шаблону HTML), яке потім розв’язується черезViewResolver.@RestController— це мета-анотація, яка об’єднує@Controllerта@ResponseBody. Її методи повертають чисті дані доменної моделі, які серіалізуються в тіло відповіді (зазвичай JSON) черезHttpMessageConverter. -
Чому ієрархія винятків
DataAccessExceptionу Spring зроблена неперевіряємою (unchecked)? Відповідь: Більшість помилок роботи з БД (обрив з’єднання, deadlock, порушення обмежень цілісності) є фатальними або неусувними на рівні виклику конкретного методу. Роблячи винятки неперевіряємими (нащадкамиRuntimeException), Spring позбавляє розробників від захаращення кожного методу примусовими блокамиtry-catchабо секціямиthrows SQLException, дозволяючи централізовано перехоплювати їх у глобальних обробниках (@ControllerAdvice).
Червоні прапорці (чого категорично не можна говорити)
- ❌ «
@Serviceавтоматично робить усі методи класу транзакційними» — ні, транзакції активуються лише анотацією@Transactional. Сам по собі@Serviceтранзакції не відкриває. - ❌ «
@Repositoryта@Componentпрацюють абсолютно однаково під капотом» —@Repositoryвмикає автоматичну трансляцію винятків. - ❌ «
@RestControllerне вміє повертати HTTP-статуси помилок» — статуси гнучко повертаються черезResponseEntity<T>або@ResponseStatus. - ❌ «Для реєстрації біна обов’язково використовувати лише
@Component» — використання профільних стереотипів (@Service,@Repository) є обов’язковим за стандартом чистої архітектури Spring.