Cette page couvre les patterns CDI 4.1 Lite supportés par Vauban et leur intégration avec les autres modules de l’écosystème. Tous les exemples se compilent ; le bytecode d’injection est entièrement généré à process-classes.
Scopes
CDI 4.1 Lite couvre quatre scopes. Vauban les supporte tous, plus @RequestScoped quand un transport contextuel est branché.
| Scope | Sémantique | Implémenté |
|---|---|---|
|
Un seul bean pour la JVM, pas de proxy. |
✅ |
|
Un seul bean par container, proxy normal-scope. |
✅ |
|
Une instance par point d’injection, pas de proxy. |
✅ (par défaut) |
|
Une instance par requête, proxy normal-scope. |
✅ (avec Cassini ou Foy actifs) |
|
Une instance par session HTTP. |
// TODO@user: confirmer le statut côté Foy / Cassini |
|
Les scopes normaux ( |
Qualifiers
Permettent de discriminer plusieurs implémentations d’un même type.
import jakarta.inject.Qualifier;
import java.lang.annotation.*;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface Audit {}
@ApplicationScoped @Audit
public class AuditLogger implements Logger { /* ... */ }
@ApplicationScoped
public class Service {
@Inject @Audit Logger logger;
}
@Named, @Default, @Any sont supportés. Les qualificateurs custom avec membres @Nonbinding également.
Producers et @Disposes
@ApplicationScoped
public class DataSourceProducer {
@Produces @ApplicationScoped
public DataSource dataSource(Config config) {
return DataSource.builder().url(config.url()).build();
}
public void close(@Disposes DataSource ds) {
ds.close();
}
}
Le @Disposes est appelé quand le bean producteur est détruit. Vauban génère pour chaque méthode producer une _ProducerFactory distincte.
Événements
public record OrderCreated(String id) {}
@ApplicationScoped
public class OrderService {
@Inject Event<OrderCreated> events;
public void create(String id) {
events.fire(new OrderCreated(id));
}
}
@ApplicationScoped
public class OrderListener {
public void onOrder(@Observes OrderCreated event) {
System.out.println("nouvelle commande " + event.id());
}
}
Les observateurs asynchrones (@ObservesAsync) sont dispatchés sur l'`Executors.newVirtualThreadPerTaskExecutor()` interne. Voir modèle de threading.
Intercepteurs
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Logged {}
@Logged
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class LoggingInterceptor {
@AroundInvoke
public Object around(InvocationContext ctx) throws Exception {
long t0 = System.nanoTime();
try {
return ctx.proceed();
} finally {
System.out.printf("%s : %d ns%n", ctx.getMethod(), System.nanoTime() - t0);
}
}
}
@ApplicationScoped @Logged
public class Service { /* méthodes interceptées */ }
@AroundConstruct est également supporté. Les chaînes d’intercepteurs sont résolues à la compilation et stockées dans la _Factory générée.
Instance<T> programmatique
@ApplicationScoped
public class Router {
@Inject Instance<Handler> handlers;
public Handler resolve(String name) {
return handlers.select(NamedLiteral.of(name)).get();
}
}
Instance<T> permet la résolution dynamique côté application, tout en gardant la grille d’injection statique côté container.
Intégration avec Cassini (Jakarta REST)
vauban-core est la couche DI sous-jacente de Cassini. Les ressources @Path sont des beans CDI ; les providers (@Provider, MessageBodyReader/Writer) également. Pas de configuration : Cassini consomme directement le BeanManager Vauban.
@Path("/orders")
@ApplicationScoped
public class OrderResource {
@Inject OrderService service;
@POST
public Response create(OrderRequest req) {
service.create(req.id());
return Response.created(URI.create("/orders/" + req.id())).build();
}
}
Intégration avec Foy (Jakarta Servlet)
Foy expose les servlets, filtres et listeners comme beans CDI. Les @RequestScoped suivent le cycle requête/réponse HTTP.
Tests JUnit avec @VaubanTest
@VaubanTest
class GreetingServiceTest {
@Inject GreetingService greeting;
@Test
void hello() {
assertEquals("Hello, Vauban!", greeting.hello("Vauban"));
}
}
L’extension boote un container minimal par classe de test. Voir la référence JUnit.