MicroProfile Config implementation for GuicedEE applications using SmallRye Config and Google Guice.
Inject configuration values with @ConfigProperty, resolve from environment variables, system properties, and META-INF/microprofile-config.properties — all wired automatically through SPI discovery and Guice bindings.
Built on SmallRye Config · MicroProfile Config · Google Guice · Vert.x · JPMS module com.guicedee.microprofile.config · Java 25+
<dependency>
<groupId>com.guicedee.microprofile</groupId>
<artifactId>config</artifactId>
</dependency>Gradle (Kotlin DSL)
implementation("com.guicedee.microprofile:config:2.0.0-SNAPSHOT")- CDI-style injection —
@ConfigProperty(name = "key")with automatic type conversion forString,Boolean,Integer,Long,Double,Float, andOptional<T>wrappers - Standards-compliant — implements
org.eclipse.microprofile.configAPIs and annotations via SmallRye Config - Deterministic source ordering — environment variables (highest), system properties, and classpath
META-INF/microprofile-config.properties(lowest) - Custom converters — register
Converter<T>implementations viaServiceLoaderfor application-specific types - Profile support — profile-specific properties (e.g.
%dev.key=value) resolved by SmallRye Config - Guice-native —
SmallRyeConfigis bound as a singleton viaSmallRyeConfigProvider;@ConfigPropertyfields are scanned and bound at startup - Vert.x-aware initialization — config is built on a Vert.x worker thread to avoid blocking the event loop
- JPMS-ready — named module with proper exports, provides, and opens directives
Step 1 — Add a microprofile-config.properties file:
# src/main/resources/META-INF/microprofile-config.properties
messaging.enabled=true
messaging.bootstrap.servers=localhost:9092
liveness.port=8081Step 2 — Inject configuration values:
import org.eclipse.microprofile.config.inject.ConfigProperty;
public class MessagingService {
@ConfigProperty(name = "messaging.enabled", defaultValue = "true")
boolean enabled;
@ConfigProperty(name = "messaging.bootstrap.servers")
String bootstrapServers;
public void start() {
if (enabled) {
// use bootstrapServers
}
}
}Step 3 — Register via JPMS:
module my.app {
requires com.guicedee.microprofile.config;
}The MicroProfileConfigContext initializes SmallRye Config automatically during IGuicePreStartup, and MicroProfileConfigBinder scans for all @ConfigProperty fields and binds them into Guice.
IGuiceContext.instance()
└─ IGuiceConfigurator hooks
└─ ScanConfig (enables field, annotation, classpath scanning)
└─ IGuicePreStartup hooks
└─ MicroProfileConfigContext (builds SmallRyeConfig on Vert.x worker thread)
├─ addDefaultSources() (env vars, system props, microprofile-config.properties)
├─ addDiscoveredSources() (ServiceLoader-discovered ConfigSource SPIs)
├─ addDiscoveredConverters() (ServiceLoader-discovered Converter<T> SPIs)
└─ addDiscoveredInterceptors() (ConfigSourceInterceptor SPIs)
└─ IGuiceModule hooks
└─ MicroProfileConfigBinder (Guice AbstractModule)
├─ bind SmallRyeConfig via SmallRyeConfigProvider (singleton)
├─ scan @ConfigProperty fields via ClassGraph
└─ bind each field type: String, Boolean, Integer, Long, Double, Float, Optional<T>
└─ InjectionPointProvider
└─ InjectionPointProvision (registers @ConfigProperty as a Guice injection point)
MicroProfile Config defines a composite of sources with numeric ordinals. Higher ordinal wins when keys overlap:
| Source | Ordinal | Example |
|---|---|---|
| Environment variables | 300 | MESSAGING_ENABLED=true |
| System properties | 200 | -Dmessaging.enabled=true |
META-INF/microprofile-config.properties |
100 | messaging.enabled=true |
Environment variable names follow MicroProfile Config rules:
- Dots (
.) and hyphens (-) are replaced with underscores (_) - Names are uppercased
- e.g.
messaging.bootstrap.servers→MESSAGING_BOOTSTRAP_SERVERS
SmallRye Config supports profile-specific properties using the %profile. prefix:
# META-INF/microprofile-config.properties
db.url=jdbc:postgresql://prod-host:5432/mydb
# Profile-specific override
%dev.db.url=jdbc:postgresql://localhost:5432/mydbActivate a profile with mp.config.profile=dev (system property or environment variable).
Inject the SmallRyeConfig instance directly for programmatic lookups:
import jakarta.inject.Inject;
import io.smallrye.config.SmallRyeConfig;
public class HealthProbe {
@Inject
SmallRyeConfig config;
public int livenessPort() {
return config.getOptionalValue("liveness.port", Integer.class).orElse(8081);
}
}Or use the standard MicroProfile Config interface:
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
Config config = ConfigProvider.getConfig();
String value = config.getValue("messaging.enabled", String.class);MicroProfileConfigBinder scans all classes with @ConfigProperty-annotated fields and creates Guice bindings for each:
| Field type | Binding |
|---|---|
String |
Direct value from config |
boolean / Boolean |
Parsed via Boolean.parseBoolean() |
int / Integer |
Parsed via Integer.parseInt() |
long / Long |
Parsed via Long.parseLong() |
double / Double |
Parsed via Double.parseDouble() |
float / Float |
Parsed via Float.parseFloat() |
Optional<String> |
Wrapped optional |
Optional<Boolean> |
Wrapped optional |
Optional<Integer> |
Wrapped optional |
Optional<Long> |
Wrapped optional |
Optional<Double> |
Wrapped optional |
Optional<Float> |
Wrapped optional |
Default values are supported via @ConfigProperty(defaultValue = "...").
Register custom Converter<T> implementations via ServiceLoader for application-specific types:
import org.eclipse.microprofile.config.spi.Converter;
import java.time.Duration;
public class DurationConverter implements Converter<Duration> {
@Override
public Duration convert(String value) {
if (value.endsWith("ms")) return Duration.ofMillis(Long.parseLong(value.replace("ms", "")));
if (value.endsWith("s")) return Duration.ofSeconds(Long.parseLong(value.replace("s", "")));
throw new IllegalArgumentException("Unsupported duration format: " + value);
}
}Register via META-INF/services/org.eclipse.microprofile.config.spi.Converter:
com.example.DurationConverter
And then with JPMS:
module my.app {
provides org.eclipse.microprofile.config.spi.Converter
with com.example.DurationConverter;
}All SPIs are discovered via ServiceLoader. Register implementations with JPMS provides...with or META-INF/services.
| SPI | Purpose |
|---|---|
IGuicePreStartup |
MicroProfileConfigContext — builds the SmallRyeConfig instance |
IGuiceModule |
MicroProfileConfigBinder — scans @ConfigProperty fields and binds to Guice |
IGuiceConfigurator |
ScanConfig — enables classpath, annotation, and field scanning |
InjectionPointProvider |
InjectionPointProvision — registers @ConfigProperty as an injection point |
ConfigSource (MicroProfile) |
Add custom config sources with custom ordinals |
Converter<T> (MicroProfile) |
Register custom type converters |
ConfigSourceInterceptor (SmallRye) |
Intercept and transform config values |
com.guicedee.microprofile.config
├── io.smallrye.config.core (SmallRye Config — MicroProfile Config implementation)
├── com.guicedee.vertx (Vert.x lifecycle — worker thread initialization)
├── com.guicedee.client (GuicedEE client — SPI contracts, IGuiceContext)
└── com.google.guice (Guice DI — injection bindings)
| Class | Package | Role |
|---|---|---|
MicroProfileConfigContext |
config |
IGuicePreStartup — builds SmallRyeConfig on a Vert.x worker thread at startup |
MicroProfileConfigBinder |
implementations |
IGuiceModule — scans @ConfigProperty fields and creates Guice bindings per type |
SmallRyeConfigProvider |
implementations |
Guice Provider<SmallRyeConfig> — returns the shared config instance |
InjectionPointProvision |
implementations |
InjectionPointProvider — registers @ConfigProperty for Guice injection point processing |
ScanConfig |
implementations |
IGuiceConfigurator — enables classpath, annotation, and field scanning |
Module name: com.guicedee.microprofile.config
The module:
- exports
com.guicedee.microprofile.config - requires transitive
io.smallrye.config.core,com.guicedee.vertx - provides
IGuicePreStartupwithMicroProfileConfigContext - provides
IGuiceModulewithMicroProfileConfigBinder - provides
InjectionPointProviderwithInjectionPointProvision - provides
IGuiceConfiguratorwithScanConfig - opens
com.guicedee.microprofile.config.implementationstocom.google.guice
In non-JPMS environments, META-INF/services discovery still works.
import com.guicedee.client.IGuiceContext;
import com.google.inject.Injector;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class ConfigTest {
@Test
void configPropertyFieldsAreInjected() {
Injector injector = IGuiceContext.getContext().inject();
MyConfigBean bean = injector.getInstance(MyConfigBean.class);
assertNotNull(bean.getServerHost());
}
}Provide test configuration in src/test/resources/META-INF/microprofile-config.properties:
server.host=localhost
server.port=8080Issues and pull requests are welcome — please add tests for new type converters, config sources, or binding changes.