Какие способы внедрения зависимостей (Dependency Injection) существуют в Spring?

«Какие способы внедрения зависимостей (Dependency Injection) существуют в Spring?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Spring предоставляет три основных способа внедрения зависимостей (DI):

  1. Внедрение через конструктор (Constructor Injection) — рекомендуется.

    • Почему: Гарантирует неизменяемость зависимостей (final поля), обеспечивает полную инициализацию объекта и упрощает тестирование.
    • Как: Аннотация @Autowired на конструкторе (необязательна с Spring 4.3 для конструкторов с одним параметром).
      @Service
      public class UserService {
      private final UserRepository repository;
      // @Autowired // Опционально
      public UserService(UserRepository repository) {
          this.repository = repository;
      }
      }
  2. Внедрение через сеттер (Setter Injection).

    • Почему: Позволяет изменять зависимость после создания бина. Подходит для опциональных зависимостей.
    • Как: Аннотация @Autowired на методе-сеттере.
      @Service
      public class OrderService {
      private PaymentService paymentService;
      @Autowired
      public void setPaymentService(PaymentService paymentService) {
          this.paymentService = paymentService;
      }
      }
  3. Внедрение через поле (Field Injection) — не рекомендуется.

    • Почему: Нарушает инкапсуляцию, усложняет тестирование (требует Spring-контекста или ReflectionTestUtils), скрывает обязательность зависимости.
    • Как: Аннотация @Autowired непосредственно на поле.
      @Service
      public class ReportService {
      @Autowired
      private DataSource dataSource; // Избегайте этого стиля
      }

Best Practice: Для обязательных зависимостей всегда используйте внедрение через конструктор. Это подход, продвигаемый Spring и сообществом, как наиболее надежный и соответствующий принципам иммутабельности.