Cette page montre comment écrire un premier bean @ApplicationScoped, le câbler via @Inject, et démarrer le container. La compilation génère tout le bytecode d’injection : aucun proxy dynamique, aucune réflexion au runtime.

Pré-requis

  • Java 25 — Temurin recommandé. Module path activé.

  • Maven 3.9.16 — épinglé via .sdkmanrc à la racine du repo.

  • JPMS strict : un module-info.java par module Maven.

Coordonnées Maven

<dependencies>
    <dependency>
        <groupId>io.vidocq.vauban</groupId>
        <artifactId>vauban-api</artifactId>
        <version>0.1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>io.vidocq.vauban</groupId>
        <artifactId>vauban-core</artifactId>
        <version>0.1.0-SNAPSHOT</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.vidocq.vauban</groupId>
        <artifactId>vauban-processor</artifactId>
        <version>0.1.0-SNAPSHOT</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>io.vidocq.vauban</groupId>
      <artifactId>vauban-maven-plugin</artifactId>
      <version>0.1.0-SNAPSHOT</version>
      <executions>
        <execution>
          <goals>
            <goal>generate</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Premier bean @ApplicationScoped

package io.example;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingService {
    public String hello(String name) {
        return "Hello, " + name + "!";
    }
}

Premier @Inject

package io.example;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
public class HelloApp {

    @Inject GreetingService greeting;

    public void run() {
        System.out.println(greeting.hello("Vauban"));
    }
}

L’injection par champ et l’injection par constructeur sont supportées. L’injection par constructeur est recommandée pour faciliter les tests unitaires hors container.

module-info.java minimal

module io.example {
    requires jakarta.cdi;
    requires io.vidocq.vauban.api;
    exports io.example;
}

Bootstrap du container

import io.vidocq.vauban.api.Vauban;

void main() {
    try (var container = Vauban.bootstrap()) {
        var app = container.select(HelloApp.class).get();
        app.run();
    }
}

try-with-resources ferme proprement le container : les @PreDestroy sont appelés, les contextes purgés, les événements @BeforeDestroyed(ApplicationScoped.class) puis @Destroyed(ApplicationScoped.class) sont émis.

Build et exécution

sdk env
./mvnw -ntp install -DskipTests
java --module-path target/modules --module io.example/io.example.Main

Le plugin Maven a généré dans target/generated-sources/ les _Factory correspondant à chaque bean. Aucune réflexion ne tournera au démarrage : Vauban se contente d’instancier ces factories puis d’appeler les méthodes générées.

Trois modes de découverte des beans

Vauban supporte trois stratégies, selon le contexte :

Mode Usage API

scanLocal()

Application standard mono-module

Vauban.builder().scanLocal().build()

scanClasspath()

Application multi-modules avec dépendances CDI transitives

Vauban.builder().scanClasspath().build()

addBeanClass(…​)

Tests unitaires, sélection programmatique

Vauban.builder().addBeanClass(MyBean.class).build()

Étape suivante