Ответ
Spring реализует Dependency Injection (DI) через свой IoC-контейнер, который автоматически управляет зависимостями между бинами. Основные способы внедрения:
1. Внедрение через конструктор (Constructor Injection) — рекомендуемый способ
Зависимости передаются в качестве аргументов конструктора. Делает зависимости явными и обязательными, обеспечивает неизменяемость и удобство тестирования.
@Service
public class UserService {
private final UserRepository userRepo;
// Spring автоматически найдет бин UserRepository и внедрит его
@Autowired // Начиная с Spring 4.3, для одного конструктора аннотация не обязательна
public UserService(UserRepository userRepo) {
this.userRepo = userRepo;
}
}
2. Внедрение через сеттер (Setter Injection)
Зависимости устанавливаются через setter-методы. Полезно для опциональных зависимостей или изменения зависимостей в runtime.
@Service
public class OrderService {
private DiscountCalculator discountCalculator;
@Autowired
public void setDiscountCalculator(DiscountCalculator calc) {
this.discountCalculator = calc;
}
}
3. Внедрение через поле (Field Injection) — используйте с осторожностью
Зависимость инжектируется напрямую в поле с помощью @Autowired. Наиболее лаконичный, но наименее гибкий способ (усложняет тестирование, скрывает зависимости).
@Repository
public class UserRepositoryImpl {
@Autowired
private JdbcTemplate jdbcTemplate;
}
Как это работает под капотом:
- Spring создает контекст приложения, сканируя классы и конфигурации.
- Для каждого бина анализируются его зависимости (по типу, имени, аннотациям
@Qualifier). - Контейнер строит граф зависимостей, разрешает их (находит или создает требуемые бины) и внедряет их в целевые бины перед тем, как отдать их в использование.
- Внедрение может происходить через рефлексию или генерацию прокси-классов (для бинов со scope
prototypeили при использовании@Lookup).
Преимущества DI в Spring: снижение связанности кода, упрощение тестирования (зависимости легко подменить mock-объектами), централизованное управление конфигурацией.