В чём разница между @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. Что делать, если есть несколько бинов одного типа]]