← Назад к вопросам

Что такое DI в Spring?

1.0 Junior🔥 121 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Dependency Injection (DI) в Spring

Dependency Injection (внедрение зависимостей) — это фундаментальный паттерн проектирования в Spring Framework, который позволяет управлять зависимостями между компонентами приложения и создавать слабо связанный, тестируемый и поддерживаемый код.

Основная концепция

Вместо того чтобы объект сам создавал свои зависимости, они передаются (внедряются) в него извне. Это решение принимает Spring контейнер.

// ❌ Неправильно: жесткая связь
public class UserService {
    private UserRepository repository = new UserRepository();
    
    public User getUser(Long id) {
        return repository.findById(id);
    }
}

// ✅ Правильно: зависимость внедряется
public class UserService {
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
    
    public User getUser(Long id) {
        return repository.findById(id);
    }
}

Способы внедрения зависимостей

1. Constructor Injection (Рекомендуемый способ)

Зависимость передается через конструктор:

@Service
public class UserService {
    private final UserRepository userRepository;
    private final MailService mailService;
    
    // Spring автоматически внедрит зависимости
    public UserService(UserRepository userRepository, MailService mailService) {
        this.userRepository = userRepository;
        this.mailService = mailService;
    }
}

Преимущества:

  • Зависимости видны явно в конструкторе
  • Объект создается с полностью инициализированным состоянием
  • Легко тестировать (можно передать mock объекты)
  • Гарантирует immutability (final поля)

2. Setter Injection

Зависимость устанавливается через setter методы с аннотацией @Autowired:

@Service
public class UserService {
    private UserRepository userRepository;
    private MailService mailService;
    
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Autowired
    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }
}

Недостатки:

  • Зависимости не видны при создании объекта
  • Объект может быть частично инициализирован
  • Сложнее для тестирования

3. Field Injection

Зависимость внедряется прямо в поле с аннотацией @Autowired:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private MailService mailService;
}

Недостатки:

  • Сложно тестировать (нельзя легко подставить mock)
  • Нельзя использовать final поля
  • Зависимости скрыты
  • Может привести к NullPointerException

Как Spring управляет зависимостями

1. Аннотация @Component (и его специализации)

Пометить класс как управляемый компонент:

@Component
public class MyService {
    // Spring создаст бин этого класса
}

Специализированные аннотации:

  • @Service — для слоя бизнес-логики
  • @Repository — для слоя работы с БД
  • @Controller — для слоя представления
  • @Configuration — для конфигурационных классов

2. Бины (Beans)

Beans — это объекты, управляемые Spring контейнером:

@Configuration
public class AppConfig {
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
    
    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
}

3. ApplicationContext

Spring контейнер, который управляет всеми бинами и их жизненным циклом:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        UserService userService = context.getBean(UserService.class);
        userService.getUser(1L);
    }
}

Жизненный цикл бина

1. Instantiation (создание экземпляра)
2. Dependency Injection (внедрение зависимостей)
3. Initialization (@PostConstruct)
4. Ready for use (готов к использованию)
5. Destruction (@PreDestroy)
@Service
public class UserService implements InitializingBean, DisposableBean {
    
    @PostConstruct
    public void init() {
        System.out.println("Инициализация сервиса");
    }
    
    @PreDestroy
    public void destroy() {
        System.out.println("Уничтожение сервиса");
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {}
    
    @Override
    public void destroy() throws Exception {}
}

Квалификаторы при наличии нескольких реализаций

Если существует несколько бинов одного типа, используй @Qualifier:

public interface PaymentService {}

@Component
@Qualifier("paypalPayment")
public class PayPalPaymentService implements PaymentService {}

@Component
@Qualifier("stripePayment")
public class StripePaymentService implements PaymentService {}

@Service
public class OrderService {
    private final PaymentService paymentService;
    
    public OrderService(@Qualifier("stripePayment") PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

Преимущества DI

  1. Слабая связь — компоненты независимы друг от друга
  2. Тестируемость — легко подставлять mock объекты
  3. Гибкость — легко менять реализации
  4. Читаемость — явные зависимости
  5. Масштабируемость — проще управлять сложными приложениями

Пример полного приложения со Spring DI

public interface UserRepository {
    User findById(Long id);
}

@Repository
public class UserRepositoryImpl implements UserRepository {
    @Override
    public User findById(Long id) {
        return new User(id, "John Doe");
    }
}

public interface MailService {
    void sendEmail(String to, String message);
}

@Service
public class MailServiceImpl implements MailService {
    @Override
    public void sendEmail(String to, String message) {
        System.out.println("Email sent to " + to);
    }
}

@Service
public class UserService {
    private final UserRepository userRepository;
    private final MailService mailService;
    
    public UserService(UserRepository userRepository, MailService mailService) {
        this.userRepository = userRepository;
        this.mailService = mailService;
    }
    
    public void registerUser(Long userId) {
        User user = userRepository.findById(userId);
        mailService.sendEmail(user.getEmail(), "Welcome!");
    }
}

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Dependency Injection в Spring — это не просто паттерн, это основа архитектуры всего фреймворка, обеспечивающая чистоту кода и его поддерживаемость.