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

Как реализуется Inversion of Control через Dependency Injection в Quarkus?

2.2 Middle🔥 171 комментариев
#Spring Framework

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

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

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

Inversion of Control и Dependency Injection в Quarkus

Inversion of Control (IoC) — это архитектурный паттерн, при котором управление потоком управления передаётся фреймворку. Dependency Injection (DI) — реализация IoC, когда зависимости объекта передаются извне, а не создаются самим объектом. Quarkus использует собственный DI контейнер на базе ArC (Arc is a CDI container).

1. Базовый DI в Quarkus

Quarkus использует стандарт Jakarta CDI (Contexts and Dependency Injection) с расширениями. Вот как это работает:

// 1. Bean-сервис
@ApplicationScoped
public class UserService {
    public String getUsername(Long userId) {
        return "User" + userId;
    }
}

// 2. Инъекция зависимости
@Path("/users")
public class UserResource {
    @Inject
    UserService userService; // Quarkus сам создаст экземпляр!
    
    @GET
    @Path("/{id}")
    public String getUser(@PathParam("id") Long id) {
        return userService.getUsername(id);
    }
}

// Quarkus автоматически:
// - Создаст singleton UserService
// - Инъектирует его в UserResource
// - Управляет жизненным циклом

2. Scopes (Области видимости)

Quarkus поддерживает различные scopes для управления жизненным циклом:

// @ApplicationScoped — один экземпляр на всё приложение
@ApplicationScoped
public class DatabaseConnection {
    // Инициализируется один раз при старте
}

// @Singleton — синоним @ApplicationScoped
@Singleton
public class CacheManager { }

// @RequestScoped — новый экземпляр на каждый HTTP запрос
@RequestScoped
public class RequestContext {
    public long createdAt = System.currentTimeMillis();
}

// @Dependent — новый экземпляр при каждой инъекции (по умолчанию)
@Dependent
public class RequestLogger {
    public void log(String msg) { System.out.println(msg); }
}

3. Инъекция через конструктор (рекомендуется)

В современном Quarkus лучше использовать инъекцию через конструктор вместо @Inject на поле:

// Плохо: инъекция на поле
@RequestScoped
public class OrderService {
    @Inject
    OrderRepository repository; // Сложнее тестировать
}

// Хорошо: инъекция через конструктор
@RequestScoped
public class OrderService {
    private final OrderRepository repository;
    
    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }
    
    public Order getOrder(Long id) {
        return repository.findById(id);
    }
}

// Quarkus найдёт конструктор и инъектирует все зависимости

4. Qualifiers (Квалификаторы)

Если несколько реализаций одного интерфейса, используй qualifiers:

// Интерфейс
public interface PaymentService {
    void processPayment(BigDecimal amount);
}

// Первая реализация
@ApplicationScoped
@Named("card")
public class CardPaymentService implements PaymentService {
    @Override
    public void processPayment(BigDecimal amount) {
        System.out.println("Paying " + amount + " by card");
    }
}

// Вторая реализация
@ApplicationScoped
@Named("crypto")
public class CryptoPaymentService implements PaymentService {
    @Override
    public void processPayment(BigDecimal amount) {
        System.out.println("Paying " + amount + " by crypto");
    }
}

// Использование
@Path("/payment")
public class PaymentResource {
    private final PaymentService cardPayment;
    private final PaymentService cryptoPayment;
    
    public PaymentResource(
        @Named("card") PaymentService cardPayment,
        @Named("crypto") PaymentService cryptoPayment
    ) {
        this.cardPayment = cardPayment;
        this.cryptoPayment = cryptoPayment;
    }
}

5. Factories (Фабрики для сложных объектов)

Для создания сложных объектов используй producer methods:

@ApplicationScoped
public class BeanProducers {
    @Produces
    @ApplicationScoped
    public DatabaseConnection createDatabase() {
        DatabaseConnection conn = new DatabaseConnection();
        conn.connect("jdbc:postgresql://localhost:5432/mydb");
        return conn;
    }
    
    @Produces
    public RestClient createRestClient(DatabaseConnection db) {
        return new RestClient(db.getConfig());
    }
}

// Теперь DatabaseConnection инъектируется как обычный bean
@RequestScoped
public class DataService {
    private final DatabaseConnection connection;
    
    public DataService(DatabaseConnection connection) {
        this.connection = connection;
    }
}

6. Жизненный цикл beans

Quarkus вызывает специальные методы при создании/уничтожении beans:

@ApplicationScoped
public class ResourcePool {
    private List<Connection> connections;
    
    @PostConstruct
    public void initialize() {
        // Вызывается ПОСЛЕ создания bean'а
        connections = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            connections.add(new Connection());
        }
        System.out.println("Pool initialized with 10 connections");
    }
    
    @PreDestroy
    public void cleanup() {
        // Вызывается ПЕРЕД уничтожением bean'а (при shutdown)
        connections.forEach(Connection::close);
        System.out.println("Pool cleaned up");
    }
    
    public Connection getConnection() {
        return connections.get(0);
    }
}

7. Тестирование с DI

Quarkus упрощает тестирование:

@QuarkusTest
class UserResourceTest {
    @Inject
    UserService userService;
    
    @Test
    void testGetUser() {
        String username = userService.getUsername(1L);
        assertEquals("User1", username);
    }
}

// Или с mock'ом
@QuarkusTest
class OrderServiceTest {
    @InjectMock
    OrderRepository mockRepository;
    
    @Inject
    OrderService orderService;
    
    @Test
    void testGetOrder() {
        when(mockRepository.findById(1L))
            .thenReturn(new Order(1L, "Test Order"));
        
        Order result = orderService.getOrder(1L);
        assertNotNull(result);
    }
}

8. Lazy loading в Quarkus

Можешь отложить инициализацию bean'а:

@ApplicationScoped
public class HeavyService {
    public HeavyService() {
        System.out.println("Initializing expensive resources...");
        // Долгая инициализация
    }
}

// Инъекция Lazy
@RequestScoped
public class MyResource {
    private final Lazy<HeavyService> heavyService;
    
    public MyResource(Lazy<HeavyService> heavyService) {
        this.heavyService = heavyService;
    }
    
    @GET
    public void doSomething() {
        // heavyService инициализируется только здесь
        heavyService.get().doWork();
    }
}

Итоги

IoC/DI в Quarkus работает следующим образом:

  • ArC контейнер управляет жизненным циклом beans
  • @Inject/@ApplicationScoped — основные аннотации
  • Scopes определяют, как долго живёт bean
  • Qualifiers разрешают конфликты при нескольких реализациях
  • Factories помогают с создание сложных объектов
  • @PostConstruct/@PreDestroy для инициализации/очистки
  • Тестирование упрощено встроенными инструментами

Ключевое преимущество: Quarkus компилирует DI граф во время build-time, а не runtime, что делает приложения быстрыми и потребляющими мало памяти — идеально для микросервисов и serverless.

Как реализуется Inversion of Control через Dependency Injection в Quarkus? | PrepBro