How to Implement the Reloadable Interface for Manual Property Reload in Owner
Extend Reloadable alongside Config and call reload() on your configuration instance to refresh properties at runtime without restarting your application.
The Owner library provides type-safe access to Java properties through dynamic proxies. When you need to pick up external configuration changes on demand, you must implement the Reloadable interface for manual property reload capabilities. This approach complements automatic hot-reload features by giving you explicit control over exactly when configuration refreshes occur.
Understanding the Reloadable Interface
The Reloadable interface in org.aeonbits.owner extends Config and declares three methods that Owner implements dynamically at runtime. When you invoke ConfigFactory.create(), the framework generates a proxy that handles these methods—you provide no method bodies.
The interface defines these operations:
void reload()– Re-executes the loading pipeline to refresh cached property values.void addReloadListener(ReloadListener listener)– Registers a callback invoked after each reload.void removeReloadListener(ReloadListener listener)– Unregisters a previously added listener.
According to the source code in [Reloadable.java](https://github.com/matteobaccan/owner/blob/master/owner/src/main/java/org/aeonbits/owner/Reloadable.java), these methods enable programmatic control over configuration refresh cycles while maintaining type safety.
Step-by-Step Implementation
Follow these steps to implement the Reloadable interface for manual property reload:
- Define your interface by extending both
ConfigandReloadable. - Annotate with sources using
@Sourcesto specify property file locations. - Create the proxy via
ConfigFactory.create(MyConfig.class). - Trigger reloads by calling
cfg.reload()when external changes occur. - Manage observers by registering
ReloadListenerinstances to react to refresh events.
Code Examples
Basic Reloadable Configuration
Define an interface that extends Reloadable and map your properties:
import org.aeonbits.owner.Config;
import org.aeonbits.owner.Config.Sources;
import org.aeonbits.owner.Reloadable;
@Sources("file:./my.properties")
public interface MyConfig extends Config, Reloadable {
int timeout();
String endpoint();
}
Use the configuration and trigger manual reloads at runtime:
import org.aeonbits.owner.ConfigFactory;
public class ConfigDemo {
public static void main(String[] args) {
MyConfig cfg = ConfigFactory.create(MyConfig.class);
System.out.println("Initial timeout: " + cfg.timeout());
// External process modifies ./my.properties...
cfg.reload(); // Manual reload
System.out.println("After reload: " + cfg.timeout());
}
}
This pattern mirrors the implementation verified in [ReloadTest.java](https://github.com/matteobaccan/owner/blob/master/owner/src/test/java/org/aeonbits/owner/reload/ReloadTest.java#L63-L66).
Reloading with Mutable Properties
When importing properties programmatically, reload() refreshes from the current state of the Properties object:
Properties overrides = new Properties();
overrides.setProperty("timeout", "30");
MyConfig cfg = ConfigFactory.create(MyConfig.class, overrides);
System.out.println(cfg.timeout()); // Output: 30
overrides.setProperty("timeout", "45");
cfg.reload(); // Forces re-read of imports
System.out.println(cfg.timeout()); // Output: 45
This technique is demonstrated in ReloadTest.testReloadWithImportedProperties.
Observing Reload Events
Register listeners to execute logic when configurations refresh:
import org.aeonbits.owner.event.ReloadListener;
import org.aeonbits.owner.event.ReloadEvent;
ReloadListener logger = event -> System.out.println(
"Reload performed on " + event.getSource());
MyConfig cfg = ConfigFactory.create(MyConfig.class);
cfg.addReloadListener(logger);
cfg.reload(); // Listener invoked
cfg.removeReloadListener(logger);
cfg.reload(); // Listener not invoked
The ReloadEvent object provides access to the source configuration instance via getSource(). This behavior is verified in [ReloadTest.java](https://github.com/matteobaccan/owner/blob/master/owner/src/test/java/org/aeonbits/owner/reload/ReloadTest.java#L8-L26).
Combining with Automatic Hot-Reload
Combine Reloadable with the @HotReload annotation for hybrid refresh strategies:
import org.aeonbits.owner.Config.HotReload;
@Sources("file:./dynamic.properties")
@HotReload(2) // Check every 2 seconds
public interface LiveConfig extends Config, Reloadable {
String value();
}
When using @HotReload, Owner automatically invokes reload() on the schedule you specify. You can still call reload() manually for immediate updates. This interaction is documented in [reload.md](https://github.com/matteobaccan/owner/blob/master/owner-site/site/docs/reload.md#L18-L30).
Internal Architecture of Manual Reload
When you invoke reload(), Owner delegates to HotReloadLogic, the same component used by the automatic hot-reload feature. The process follows this sequence:
- Pipeline Re-execution: Owner re-runs the loading logic, including source resolution, variable expansion, and type conversion.
- Cache Update: The dynamic proxy updates its internal
Propertiessnapshot with new values. - Listener Notification: For each registered
ReloadListener, Owner fires aReloadEventcontaining the configuration source.
This architecture ensures that manual reloads behave identically to automatic ones, maintaining consistency across the configuration lifecycle.
Summary
- Extend
ReloadablealongsideConfigto enable manual reload capabilities in your configuration interfaces. - Call
reload()to refresh properties on-demand without recreating the configuration instance. - Use
addReloadListener()andremoveReloadListener()to observe reload events and trigger side effects. - The implementation is generated at runtime by
ConfigFactory.create(); you define only the interface structure. - Manual reload shares the same
HotReloadLogicpath as automatic reloading, ensuring consistent behavior.
Frequently Asked Questions
Do I need to implement the methods in the Reloadable interface?
No. The Owner framework generates the implementation dynamically when you call ConfigFactory.create(). The proxy handles reload(), addReloadListener(), and removeReloadListener() internally using the loading pipeline defined by your @Sources annotations.
Can I use Reloadable with immutable Properties objects?
Yes, but you will only see value changes if the underlying source data changes. If you pass a Properties object to ConfigFactory.create(), calling reload() re-reads from that same object. For immutable sources like classpath files, ensure the file content changes on disk before invoking reload().
How do I unregister a ReloadListener?
Call removeReloadListener(ReloadListener listener) on your configuration instance, passing the same listener object you previously registered via addReloadListener(). This is useful for preventing memory leaks when configuration objects have long lifecycles but listeners become obsolete.
What is the difference between @HotReload and manual reload?
The @HotReload annotation schedules automatic reload() calls at a fixed interval, while manual reload requires explicit invocation of the method. You can use both simultaneously: automatic reload handles periodic updates, while manual reload provides immediate consistency when you know external changes have occurred.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →