Vue d’ensemble

Les expressions de propriété vous permettent de référencer d’autres propriétés de configuration au sein d’une valeur en utilisant la syntaxe ${key}. Celle-ci est évaluée avant la conversion de type et permet la composition et les valeurs par défaut.

Syntaxe de base

Référencer une autre propriété

app.name=MyApp
app.full.name=${app.name} v2.0
String fullName = config.getValue("app.full.name", String.class);
// Résultat : "MyApp v2.0"

Avec valeur par défaut

app.name=MyApp
app.full.name=${app.name} ${app.version:1.0.0}
String fullName = config.getValue("app.full.name", String.class);
// Si app.version n'est pas défini, utilise la valeur par défaut "1.0.0"
// Résultat : "MyApp 1.0.0"

Expressions complexes

Combinez plusieurs références dans une seule valeur :

app.db.host=localhost
app.db.port=5432
app.db.name=mydb
app.db.url=jdbc:postgresql://${app.db.host}:${app.db.port}/${app.db.name}
String dbUrl = config.getValue("app.db.url", String.class);
// Résultat : "jdbc:postgresql://localhost:5432/mydb"

Expressions avec profils

Les expressions sont résolues après la correspondance des profils :

# Configuration de base
app.db.host=prod-db.example.com
%dev.app.db.host=localhost

# L'expression utilise la valeur résolue
app.db.url=jdbc:postgresql://${app.db.host}:5432/mydb
java -Dmp.config.profile=dev MyApplication
String dbUrl = config.getValue("app.db.url", String.class);
// Avec le profil dev actif : l'hôte résolu est "localhost"
// Résultat : "jdbc:postgresql://localhost:5432/mydb"

Détection de cycle

Si une expression de propriété se référence elle-même (directement ou indirectement), une IllegalArgumentException est levée :

# Cycle direct
app.value=${app.value}

# Cycle indirect
app.a=${app.b}
app.b=${app.a}
try {
    config.getValue("app.value", String.class);
} catch (IllegalArgumentException e) {
    System.err.println("Cycle détecté : " + e.getMessage());
}

Ravel détecte les cycles en utilisant un ScopedValue<Set<String>> pour suivre les clés en cours de résolution.

Notes de performance

  • Évaluation paresseuse — les expressions sont résolues uniquement quand la valeur est demandée

  • Pas de résolution répétée — la même expression dans une seule lookup n’est pas réévaluée

  • Thread-safeScopedValue est compatible avec les virtual threads et supporte la concurrence structurée

Exemples

Composition multi-niveaux

company=Acme
department=Engineering
team.email=${company}-${department}@example.com

Résultat : team.email = Acme-Engineering@example.com

Drapeaux de fonctionnalité avec défauts

app.features.cache=true
app.cache.ttl=${app.cache.ttl:3600}
app.cache.maxsize=${app.cache.maxsize:1000}

URLs spécifiques à l’environnement

# Base
app.api.scheme=https
app.api.host=api.example.com
app.api.port=443
app.api.url=${app.api.scheme}://${app.api.host}:${app.api.port}/v1

# Surcharge dev
%dev.app.api.scheme=http
%dev.app.api.host=localhost
%dev.app.api.port=8080

Résultat (profil dev) : - ${app.api.scheme} → http - ${app.api.host} → localhost - ${app.api.port} → 8080 - app.api.urlhttp://localhost:8080/v1

Suivant