How to Disable Variable Expansion or Parameter Formatting with @DisableFeature in Owner

Use the @DisableFeature annotation to selectively turn off automatic variable expansion (${…}) or parameter formatting (%s, %d) at either the method or class level in the Owner configuration library.

The Owner library (matteobaccan/owner) automatically processes configuration values by expanding variables like ${property} and formatting strings using String.format. When you need raw, unprocessed values, the @DisableFeature annotation provides fine-grained control over these behaviors through the Config.DisableableFeature enum.

Understanding @DisableFeature and DisableableFeature

The @DisableFeature annotation is defined in owner/src/main/java/org/aeonbits/owner/Config.java alongside the DisableableFeature enum (lines 274-277 and 284-286). This enum defines two constants you can disable:

  • VARIABLE_EXPANSION – Controls substitution of ${…} placeholders in property keys and values
  • PARAMETER_FORMATTING – Controls String.format processing for method parameters containing format specifiers like %s or %d

The annotation accepts an array of DisableableFeature values, allowing you to disable one or both features simultaneously on methods or types.

Disabling Variable Expansion

When you disable VARIABLE_EXPANSION, Owner bypasses the expandKey and expandVariables methods in PropertiesInvocationHandler.java (lines 105-110 and 135-139). This prevents ${…} placeholders from being replaced with referenced property values.

Apply the annotation at the method level to return literal placeholder text:

public interface GreetingConfig extends Config {
    @DefaultValue("Earth")
    String world();

    @DisableFeature(VARIABLE_EXPANSION)
    @DefaultValue("Hello ${world}.")
    String helloRaw();      // Returns "Hello ${world}." literally

    @DefaultValue("Hello ${world}.")
    String helloExpanded(); // Returns "Hello Earth."
}
GreetingConfig cfg = ConfigFactory.create(GreetingConfig.class);
System.out.println(cfg.helloRaw());      // Output: Hello ${world}.
System.out.println(cfg.helloExpanded()); // Output: Hello Earth.

Disabling Parameter Formatting

Parameter formatting uses String.format to inject method arguments into property values. When disabled via PARAMETER_FORMATTING, the format method in PropertiesInvocationHandler.java (lines 112-119) returns the original value unchanged.

Disable formatting to preserve format specifiers as literal text:

public interface MessageConfig extends Config {
    @DisableFeature(PARAMETER_FORMATTING)
    @DefaultValue("Hello %s.")
    String raw(String name);      // Returns "Hello %s." literally

    @DefaultValue("Hello %s.")
    String formatted(String name); // Returns "Hello Luigi."
}
MessageConfig cfg = ConfigFactory.create(MessageConfig.class);
System.out.println(cfg.raw("Luigi"));      // Output: Hello %s.
System.out.println(cfg.formatted("Luigi")); // Output: Hello Luigi.

Disabling Both Features at Class Level

Apply @DisableFeature to the interface declaration to affect all methods. This is useful when you need raw configuration values throughout your application.

@DisableFeature({VARIABLE_EXPANSION, PARAMETER_FORMATTING})
public interface MixedConfig extends Config {
    @DefaultValue("Earth")
    String planet();

    @DefaultValue("Hello %s, welcome on ${planet}!")
    String greeting(String name);   // Returns literal with both placeholders
}
MixedConfig cfg = ConfigFactory.create(MixedConfig.class);
System.out.println(cfg.greeting("Luigi")); 
// Output: Hello %s, welcome on ${planet}!

How the Disabling Mechanism Works

The Owner library implements feature disabling through the Util.isFeatureDisabled method in owner/src/main/java/org/aeonbits/owner/util/Util.java (lines 28-33). This utility checks both the method and its declaring class for the @DisableFeature annotation:

public static boolean isFeatureDisabled(Method method, DisableableFeature feature) {
    DisableFeature annotation = method.getAnnotation(DisableFeature.class);
    if (annotation == null) 
        annotation = method.getDeclaringClass().getAnnotation(DisableFeature.class);
    return annotation != null && Arrays.asList(annotation.value()).contains(feature);
}

In PropertiesInvocationHandler.java, this check determines whether to skip processing:

  • Key expansion (lines 105-110): Returns the key unmodified if VARIABLE_EXPANSION is disabled
  • Value formatting (lines 112-119): Returns the value unmodified if PARAMETER_FORMATTING is disabled
  • Variable expansion (lines 135-139): Skips placeholder substitution if VARIABLE_EXPANSION is disabled

The test suite in owner/src/test/java/org/aeonbits/owner/DisableFeatureTest.java verifies these behaviors for both method-level and class-level annotations.

Summary

  • Use @DisableFeature(VARIABLE_EXPANSION) to prevent ${…} placeholder substitution in property keys and values
  • Use @DisableFeature(PARAMETER_FORMATTING) to skip String.format processing for method parameters
  • Apply the annotation to individual methods or to the entire interface class to affect all methods
  • The feature check is implemented in Util.isFeatureDisabled and honored in PropertiesInvocationHandler for key expansion, variable expansion, and parameter formatting

Frequently Asked Questions

Can I disable features for the entire configuration interface?

Yes. Apply @DisableFeature at the class level on your interface that extends Config. This disables the specified features for all methods in that interface. The Util.isFeatureDisabled method checks both the method and its declaring class, so class-level annotations are automatically inherited by all methods unless overridden by method-specific annotations.

What happens to ${...} placeholders when variable expansion is disabled?

When VARIABLE_EXPANSION is disabled via @DisableFeature, Owner returns the property value exactly as defined in the source. The ${...} syntax remains literal text rather than being replaced by referenced property values. This bypasses the expandVariables method in PropertiesInvocationHandler.java (lines 135-139) and the expandKey method (lines 105-110).

Does @DisableFeature affect hot reloading or other Owner features?

No. The @DisableFeature annotation specifically targets only variable expansion and parameter formatting logic within PropertiesInvocationHandler. It does not impact other Owner capabilities such as hot reloading, event listeners, or variable interpolation strategies. The annotation is checked only during the method invocation handling phase when resolving property values in the invoke method flow.

How do I verify that a feature is actually disabled?

You can verify the disabled state by testing method return values against expected literal strings. For variable expansion, configure a property with a ${...} reference and ensure the raw placeholder appears in the output rather than the expanded value. For parameter formatting, pass arguments to a method with %s or similar specifiers and verify the format string returns unchanged. The test suite in DisableFeatureTest.java demonstrates these verification patterns for both method-level and class-level annotations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →