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

Next

  • Usage — programmatic configuration and custom converters

  • Reference — full API documentation