Ответ
Внедрение через конструктор (Constructor Injection) — это рекомендуемый best practice в Spring.
Пример реализации:
@Service
public class UserService {
private final UserRepository userRepository;
// Начиная с Spring 4.3, аннотация @Autowired для конструктора с одним параметром не обязательна
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Почему это лучший способ:
- Неизменяемость (Immutability): Поля можно объявить как
final, гарантируя их инициализацию только один раз. - Явность зависимостей: Все зависимости видны в сигнатуре конструктора, что упрощает понимание класса.
- Упрощение тестирования: Зависимости легко передать в конструктор при unit-тестировании без необходимости Spring-контекста.
- Безопасность от NPE: Объект не может быть создан в неполностью инициализированном состоянии.
Альтернативные (менее предпочтительные) способы:
- Внедрение через поле (Field Injection): Не рекомендуется из-за сложности тестирования и скрытых зависимостей.
- Внедрение через сеттер (Setter Injection): Подходит для опциональных зависимостей, которые могут меняться в runtime.
Ответ 18+ 🔞
Слушай, а вот этот ваш "Constructor Injection" в Spring — это вообще огонь, я тебе щас объясню почему. Представь, что ты собираешь тачку. Можно, конечно, кидать детали в салон через открытое окно и надеяться, что они сами как-нибудь приварятся к двигателю — это как Field Injection. А можно взять и с самого начала, на заводе, прикрутить мотор болтами нахуй, да так, чтобы он уже никуда не делся. Вот это и есть внедрение через конструктор.
Смотри, как это выглядит, без всякой ерунды:
@Service
public class UserService {
private final UserRepository userRepository; // final, Карл! Намертво!
// Спринг 4.3 и выше такой конструктор сам видит и всё понимает. @Autowired можно даже не писать.
public UserService(UserRepository userRepository) {
this.userRepository = userRepository; // Всё, приехали. Зависимость впаяна.
}
}
И вот почему это пиздец как правильно:
-
Неизменяемость, ёпта! Поле
final— это как приговор. Один раз установил в конструкторе и всё, пиши пропало. Никакой шальной метод или рефлексия не сможет потом подсунуть тебе левый репозиторий. Объект родился цельным, как ебучка скала. -
Всё на виду, как в витрине. Открываешь конструктор — и сразу видишь, без чего этот сервис жить не может. Никаких сюрпризов, никаких скрытых инъекций через магию. Прозрачно, как слёзы ребёнка, которого только что лишили мороженого.
-
Тестирование — раз плюнуть. Хочешь протестировать? Да пожалуйста! Создаёшь мок этого самого
UserRepositoryи просто суёшь его в конструктор. Никакого Spring Context поднимать не надо, никакой чёрной магии с рефлексией. Чистый и ясный код, который не вызывает желания вырвать себе глаза. -
NPE? Не, не слышал. Объект просто физически не может существовать в полуживом состоянии. Нет репозитория — нет и сервиса. Всё честно.
А теперь, блядь, про альтернативы, которые все почему-то так любят:
-
Field Injection (внедрение в поле). Это когда ты над полем пишешь
@Autowiredи молишься, чтобы Спринг когда-нибудь туда что-нибудь засунул. А в тестах ты либо ковыряешься с рефлексией, либо пляшешь с бубном. Пиздец, а не подход. Скрытые зависимости, невозможность сделать полеfinal— один сплошной цирк. -
Setter Injection (внедрение через сеттер). Ну, это уже получше, но тоже со своими тараканами. Подходит, если зависимость опциональная и может поменяться потом, в рантайме. Но для обязательных штук — это как пристегнуть ремень безопасности уже после того, как вылетел с трассы.
Короче, вывод простой, как три копейки: делай через конструктор, не еби мозг себе и другим. Получится надёжно, прозрачно и тестируемо. А всё остальное — от лукавого, или от лени.