Vue d’ensemble

Ravel s’intègre à CDI via l’extension Build-Compatible Vauban (ravel-cdi-vauban), vous permettant d’injecter directement les valeurs de configuration dans les beans CDI avec l’annotation @ConfigProperty.

Configuration

Ajoutez la dépendance :

<dependency>
    <groupId>io.vidocq.ravel</groupId>
    <artifactId>ravel-cdi-vauban</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>

L’extension est automatiquement découverte par CDI via ServiceLoader et le mécanisme Build-Compatible Extension de Vauban.

Injection basique

import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class DatabaseConfig {

    @Inject
    @ConfigProperty("db.host")
    String host;

    @Inject
    @ConfigProperty("db.port")
    int port;

    @Inject
    @ConfigProperty("db.name")
    String database;
}

Les valeurs sont injectées à la création du bean, converties au type cible.

Avec valeurs par défaut

Utilisez l’attribut defaultValue pour fournir un repli :

@Inject
@ConfigProperty(name = "app.timeout", defaultValue = "5000")
long timeoutMs;

@Inject
@ConfigProperty(name = "app.cache.enabled", defaultValue = "true")
boolean cacheEnabled;

Si la clé de configuration n’est pas trouvée et qu’aucun defaultValue n’est fourni, le déploiement échoue avec DeploymentException.

Valeurs optionnelles

Pour la configuration optionnelle, utilisez Optional<T> :

@Inject
@ConfigProperty("feature.flag.experimental")
Optional<Boolean> experimentalFeature;

// Usage
if (experimentalFeature.isPresent()) {
    boolean enabled = experimentalFeature.get();
    // ...
}

Vous pouvez également utiliser Optional sans spécifier de nom (non recommandé, mais supporté) :

@Inject
@ConfigProperty("app.version")
Optional<String> version;

Types supportés

Tous les types supportés par les convertisseurs de types peuvent être injectés :

  • Primitifs : int, long, boolean, float, double, etc.

  • Boxés : Integer, Long, Boolean, Float, Double, etc.

  • Collections : List<T>, Set<T>, Collection<T>

  • Temporels : Duration, LocalDate, LocalDateTime, Instant, etc.

  • Types personnalisés avec convertisseurs implicites ou enregistrés

Injection par champ ou constructeur

L’injection par champ est le motif canonique pour @ConfigProperty :

@ApplicationScoped
public class Config {
    @Inject
    @ConfigProperty("key")
    String value;
}

L’injection par constructeur fonctionne également, mais est moins couramment utilisée avec la config :

@ApplicationScoped
public class Config {
    private final String value;

    @Inject
    public Config(@ConfigProperty("key") String value) {
        this.value = value;
    }
}

Injection aware des profils

@ConfigProperty respecte automatiquement les profils de configuration :

app.db.host=prod-db.example.com
%dev.app.db.host=localhost
@Inject
@ConfigProperty("app.db.host")
String dbHost;

Avec java -Dmp.config.profile=dev : - dbHost = localhost

Validation au moment du déploiement

Les valeurs de configuration sont validées au démarrage du conteneur. Si une clé requise est manquante, DeploymentException est levée :

@Inject
@ConfigProperty("app.secret.key") // Requise, pas de défaut
String secretKey;
$ java MyApplication
Exception in thread "main" jakarta.enterprise.inject.spi.DeploymentException:
  Configuration key 'app.secret.key' not found

Utilisez defaultValue ou Optional<T> pour rendre l’injection optionnelle.

Configuration groupée

Un motif courant est de grouper la config associée dans un bean @Dependent :

@Dependent
public class DatabaseConfig {
    @Inject
    @ConfigProperty("database.host")
    String host;

    @Inject
    @ConfigProperty("database.port")
    int port;

    @Inject
    @ConfigProperty("database.name")
    String name;
}

@ApplicationScoped
public class MyApplication {
    @Inject
    DatabaseConfig dbConfig;

    void start() {
        // Utiliser dbConfig.host, dbConfig.port, dbConfig.name
    }
}

Exemple : Drapeaux de fonctionnalité

@ApplicationScoped
public class Features {

    @Inject
    @ConfigProperty(name = "feature.cache", defaultValue = "true")
    boolean cacheEnabled;

    @Inject
    @ConfigProperty(name = "feature.metrics", defaultValue = "false")
    boolean metricsEnabled;

    @Inject
    @ConfigProperty(name = "feature.tracing")
    Optional<Boolean> tracingEnabled;

    public boolean isCacheEnabled() {
        return cacheEnabled;
    }

    public boolean isMetricsEnabled() {
        return metricsEnabled;
    }

    public boolean isTracingEnabled() {
        return tracingEnabled.orElse(false);
    }
}

Exemple : Client API multi-environnement

# microprofile-config.properties
api.base.url=https://api.example.com
api.timeout=5000
api.retry.attempts=3

%dev.api.base.url=http://localhost:8080
%dev.api.timeout=10000
%dev.api.retry.attempts=1
@ApplicationScoped
public class ApiClient {

    @Inject
    @ConfigProperty("api.base.url")
    String baseUrl;

    @Inject
    @ConfigProperty("api.timeout")
    long timeoutMs;

    @Inject
    @ConfigProperty(name = "api.retry.attempts", defaultValue = "3")
    int retryAttempts;

    public void call() {
        // baseUrl, timeoutMs, retryAttempts sont injectés
        // et varient par profil
    }
}

Suivant

  • Usage — configuration programmatique et convertisseurs personnalisés

  • Référence — documentation complète de l’API