Когда в Spring нужно явно указывать @Autowired, а когда можно опустить?

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

Ответ

@Autowired в Spring — аннотация для внедрения зависимостей. Начиная с Spring 4.3, во многих случаях она стала опциональной.

Когда @Autowired можно опустить:

  1. Для единственного конструктора (наиболее частый случай)

    @Service
    public class UserService {
    private final UserRepository repository;
    
    // @Autowired не нужен (Spring 4.3+)
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
    }
  2. Для setter-методов с одним параметром

    @Component
    public class NotificationService {
    private EmailSender emailSender;
    
    // @Autowired не нужен
    public void setEmailSender(EmailSender emailSender) {
        this.emailSender = emailSender;
    }
    }

Когда @Autowired обязателен или рекомендуется:

  1. Несколько конструкторов — нужно указать, какой использовать

    @Service
    public class OrderService {
    private final OrderRepository repository;
    private final PaymentService paymentService;
    
    // Этот конструктор будет использован для инъекции
    @Autowired
    public OrderService(OrderRepository repository, PaymentService paymentService) {
        this.repository = repository;
        this.paymentService = paymentService;
    }
    
    // Этот конструктор Spring проигнорирует
    public OrderService(OrderRepository repository) {
        this.repository = repository;
        this.paymentService = null;
    }
    }
  2. Полевая инъекция (field injection)

    @RestController
    public class UserController {
    @Autowired  // обязательно для полей
    private UserService userService;
    
    @Autowired
    @Qualifier("primaryDataSource")  // с Qualifier обязательно
    private DataSource dataSource;
    }
  3. Метод с несколькими параметрами

    @Configuration
    public class AppConfig {
    @Autowired  // рекомендуется для ясности
    public void configureServices(ServiceA a, ServiceB b, @Value("${timeout}") int timeout) {
        // настройка
    }
    }

Best practices Spring 5+:

// ✅ РЕКОМЕНДУЕТСЯ: инъекция через конструктор (без @Autowired)
@Service
public class ProductService {
    private final ProductRepository repository;
    private final PriceCalculator calculator;

    public ProductService(ProductRepository repository, 
                         PriceCalculator calculator) {
        this.repository = repository;
        this.calculator = calculator;
    }
}

// ⚠️ ДОПУСТИМО: инъекция через setter (для optional зависимостей)
@Component
public class ReportGenerator {
    private TemplateEngine templateEngine;

    @Autowired
    public void setTemplateEngine(TemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
    }
}

// ❌ НЕ РЕКОМЕНДУЕТСЯ: полевая инъекция (кроме @Configuration классов)
@Service
public class BadExample {
    @Autowired
    private Dependency dependency;  // сложно тестировать, не final
}

Ключевые моменты:

  • Конструкторная инъекция — предпочтительный способ для mandatory зависимостей
  • Setter-инъекция — для optional зависимостей
  • Полевая инъекция — избегать в бизнес-логике
  • Lombok — можно использовать @RequiredArgsConstructor для final полей

Почему конструкторная инъекция лучше:

  1. Поля можно сделать final (immutable)
  2. Легко тестировать (можно передавать моки в конструктор)
  3. Явные зависимости (видно все, что нужно для работы класса)
  4. Защита от циклических зависимостей (Spring обнаружит на этапе создания контекста)