# How to Load Multiple Property Files with the MERGE Load Policy in Owner

> Learn to load multiple property files with the MERGE load policy in Owner. Aggregate sources with LoadType.MERGE for unified configurations where earlier files take precedence. Optimize your Java config.

- Repository: [Matteo Baccan/owner](https://github.com/matteobaccan/owner)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Use the `@LoadPolicy(LoadType.MERGE)` annotation on your configuration interface to aggregate multiple property sources into a unified configuration, where values from the first declared source take precedence over subsequent ones.**

The Owner library (matteobaccan/owner) provides a type-safe mechanism for mapping Java properties to interfaces via annotations. When you need to compose configuration from several property sources—such as layering default settings with environment-specific overrides—the MERGE load policy combines all declared files while enforcing a predictable precedence order.

## Understanding the MERGE Load Policy

The MERGE load policy instructs Owner to load every URI specified in the `@Sources` annotation and merge them into a single `Properties` instance. Unlike the FIRST policy, which stops after locating the first available source, MERGE ensures that all sources contribute to the final configuration, with explicit rules for handling key collisions.

### How the Merging Algorithm Works

The implementation resides in [`owner/src/main/java/org/aeonbits/owner/Config.java`](https://github.com/matteobaccan/owner/blob/main/owner/src/main/java/org/aeonbits/owner/Config.java), where the `LoadType.MERGE` enum defines the loading strategy. The algorithm iterates over the source list in **reverse order** using `reverse(uris)`, meaning it processes the last declared source first. Each source is loaded via `LoadersManager.load(result, uri)`, and because earlier sources overwrite later ones, the **first source declared in your `@Sources` annotation holds the highest precedence**.

If a source cannot be read (for example, a missing file or inaccessible URL), Owner silently ignores that source and continues processing the remaining entries.

## Implementing MERGE in Your Configuration Interface

To load multiple property files with the MERGE load policy, define your configuration interface with both `@Sources` and `@LoadPolicy(LoadType.MERGE)`:

```java
@Sources({
    "classpath:org/aeonbits/owner/first.properties",
    "classpath:org/aeonbits/owner/second.properties",
    "file:${user.dir}/src/test/resources/org/aeonbits/owner/third.properties"
})
@LoadPolicy(LoadType.MERGE)
public interface MergeConfig extends Config {
    @DefaultValue("ignored")
    String foo();          // Defined in first.properties → wins
    
    @DefaultValue("ignored")
    String bar();          // Defined in second.properties → wins
    
    @DefaultValue("theDefaultValue")
    String fubar();        // Not defined in any source → default used
    
    String quux();         // Not defined in any source → null
}

```

Instantiate the configuration using `ConfigFactory`:

```java
MergeConfig cfg = ConfigFactory.create(MergeConfig.class);
System.out.println(cfg.foo());   // Prints value from first.properties
System.out.println(cfg.bar());   // Prints value from second.properties
System.out.println(cfg.fubar()); // Prints "theDefaultValue"

```

This behavior is validated in [`owner/src/test/java/org/aeonbits/owner/loadstrategies/MergeLoadStrategyTest.java`](https://github.com/matteobaccan/owner/blob/main/owner/src/test/java/org/aeonbits/owner/loadstrategies/MergeLoadStrategyTest.java), which demonstrates how the merge strategy handles multiple property files and precedence rules.

## Precedence and Conflict Resolution

When identical keys exist across multiple property files, the value from the **first declared source** in the `@Sources` array wins. Because Owner processes the array in reverse, the first source you list becomes the last one loaded, overwriting any previous values for matching keys.

For instance, if both `first.properties` and `second.properties` define `database.url`, the value from `first.properties` takes precedence because it appears first in the annotation.

## Handling Missing Properties and Defaults

When a property key is absent from all merged sources, Owner resolves the value through the following fallback mechanism:

- **Method-level default**: If the method specifies `@DefaultValue("value")`, that value is returned.
- **Null return**: If no default annotation is present, the method returns `null` (or the default value for the return type if primitives are involved).

This allows optional configuration parameters to specify safe defaults while required parameters can remain unmapped to trigger validation logic.

## Error Handling with Invalid Sources

While Owner silently ignores unreadable sources such as missing files, it strictly validates URI schemes. Specifying a malformed or unsupported protocol results in an `UnsupportedOperationException` during configuration creation:

```java
@Sources("httpz://foo.bar.baz")   // Invalid scheme
@LoadPolicy(LoadType.MERGE)
interface InvalidURLConfig extends Config {}

// Throws UnsupportedOperationException at runtime
ConfigFactory.create(InvalidURLConfig.class);

```

Ensure your source URLs use supported protocols (`classpath`, `file`, `http`, `https`, or environment variables) to avoid runtime failures.

## Summary

- The `@LoadPolicy(LoadType.MERGE)` annotation aggregates all declared property sources into a single configuration object.
- Sources are processed in reverse order, giving the first declared source in `@Sources` the highest precedence for conflicting keys.
- Unreadable sources are silently skipped, but invalid URI schemes throw `UnsupportedOperationException`.
- Missing keys fall back to `@DefaultValue` annotations or return `null`.
- The core logic is implemented in [`Config.java`](https://github.com/matteobaccan/owner/blob/main/Config.java) with comprehensive test coverage in [`MergeLoadStrategyTest.java`](https://github.com/matteobaccan/owner/blob/main/MergeLoadStrategyTest.java).

## Frequently Asked Questions

### What is the difference between MERGE and FIRST load policies in Owner?

The FIRST load policy stops after successfully loading the first available source from the `@Sources` list, treating it as the complete configuration. The MERGE policy loads all sources and combines them into a unified `Properties` object. Use FIRST for failover scenarios where you want the first existing file, and use MERGE for layering configurations where multiple files contribute values.

### How does Owner determine which value to use when the same key appears in multiple files?

Owner processes the `@Sources` array in reverse order (from last to first), so the first source declared in your annotation overwrites values loaded from subsequent sources. This reverse iteration ensures that the earliest source in your declaration has the final word on conflicting property values.

### What happens if one of the property files is missing when using MERGE?

If a source specified in `@Sources` cannot be read—for example, a file does not exist or a classpath resource is missing—Owner silently ignores that specific source and continues loading the remaining sources. This behavior allows you to specify optional configuration layers without causing the application to fail.

### Can I mix different source types in a single MERGE configuration?

Yes, the `@Sources` annotation accepts any combination of valid URIs, including `classpath:` resources, absolute or relative `file:` paths, and remote URLs. Owner's `LoadersManager` handles each protocol appropriately during the merge process, combining all successfully loaded sources regardless of their storage location.