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
}
}