Overview
Ravel integrates with CDI via Vauban Build-Compatible Extension (ravel-cdi-vauban), allowing you to inject configuration values directly into CDI beans with the @ConfigProperty annotation.
Setup
Add the dependency:
<dependency>
<groupId>io.vidocq.ravel</groupId>
<artifactId>ravel-cdi-vauban</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
The extension is automatically discovered by CDI via ServiceLoader and Vauban’s Build-Compatible Extension mechanism.
Basic injection
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;
}
The values are injected at bean creation time, converted to the target type.
With defaults
Use the defaultValue attribute to provide a fallback:
@Inject
@ConfigProperty(name = "app.timeout", defaultValue = "5000")
long timeoutMs;
@Inject
@ConfigProperty(name = "app.cache.enabled", defaultValue = "true")
boolean cacheEnabled;
If the configuration key is not found and no defaultValue is provided, deployment fails with DeploymentException.
Optional values
For optional configuration, use Optional<T>:
@Inject
@ConfigProperty("feature.flag.experimental")
Optional<Boolean> experimentalFeature;
// Usage
if (experimentalFeature.isPresent()) {
boolean enabled = experimentalFeature.get();
// ...
}
You can also use Optional without specifying a name (not recommended, but supported):
@Inject
@ConfigProperty("app.version")
Optional<String> version;
Supported types
All types supported by type converters can be injected:
-
Primitives:
int,long,boolean,float,double, etc. -
Boxes:
Integer,Long,Boolean,Float,Double, etc. -
Collections:
List<T>,Set<T>,Collection<T> -
Temporal:
Duration,LocalDate,LocalDateTime,Instant, etc. -
Custom types with implicit or registered converters
Field vs. constructor injection
Field injection is the canonical pattern for @ConfigProperty:
@ApplicationScoped
public class Config {
@Inject
@ConfigProperty("key")
String value;
}
Constructor injection also works, but is less commonly used with config:
@ApplicationScoped
public class Config {
private final String value;
@Inject
public Config(@ConfigProperty("key") String value) {
this.value = value;
}
}
Profile-aware injection
@ConfigProperty automatically respects configuration profiles:
app.db.host=prod-db.example.com
%dev.app.db.host=localhost
@Inject
@ConfigProperty("app.db.host")
String dbHost;
With java -Dmp.config.profile=dev:
- dbHost = localhost
Validation at deployment time
Configuration values are validated when the container starts. If a required key is missing, DeploymentException is thrown:
@Inject
@ConfigProperty("app.secret.key") // Required, no default
String secretKey;
$ java MyApplication
Exception in thread "main" jakarta.enterprise.inject.spi.DeploymentException:
Configuration key 'app.secret.key' not found
Use defaultValue or Optional<T> to make injection optional.
Grouped configuration
A common pattern is grouping related config in a @Dependent bean:
@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() {
// Use dbConfig.host, dbConfig.port, dbConfig.name
}
}
Example: Feature flags
@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);
}
}
Example: Multi-environment API client
# 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 are injected
// and vary by profile
}
}