@Component, @Autowired
@Component помечает класс как бин — Spring сам создаст его объект и будет им управлять. @Service, @Repository, @Controller — это специализированные версии @Component для разных слоёв приложения (по сути то же самое, но имя точнее отражает роль класса).
@Component
class EmailValidator {
boolean isValid(String email) { return email.contains("@"); }
}
@Service
class UserService {
private final EmailValidator validator;
@Autowired // "внедри сюда нужную зависимость"
UserService(EmailValidator validator) {
this.validator = validator;
}
}
@Autowired говорит Spring: «найди подходящий бин и подставь его сюда». Если бин с таким типом существует в контейнере — Spring сам передаст его в конструктор.
Копнуть глубже
@Autowired на конструкторе можно опустить, если конструктор один — современный Spring сам понимает, что нужно внедрить зависимости, без явной аннотации:
@Service
class UserService {
private final EmailValidator validator;
UserService(EmailValidator validator) { // @Autowired не нужен — конструктор единственный
this.validator = validator;
}
}
Внедрение через конструктор — рекомендуемый способ, а не через поле (@Autowired private EmailValidator validator;):
- Поле нельзя сделать
final— теряется гарантия неизменности зависимости; - Объект нельзя создать без зависимостей (в обычном
new, без Spring) — конструктор делает зависимости явными и обязательными; - Проще тестировать — просто передаёшь mock в конструктор, не нужна рефлексия.
Что если бинов подходящего типа несколько? Spring не сможет понять, какой использовать, и упадёт с ошибкой — для этого случая есть @Qualifier, разобранный отдельно в теме «Scope, @Qualifier».
• почему внедрение через конструктор предпочтительнее, чем через поле (если дошёл до 2-го слоя).