# How to Register Custom Loader Implementations for Additional File Formats in Owner

> Learn to register custom Loader implementations for new file formats in Owner Repository. Implement the Loader interface and call registerLoader before creating configurations.

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

---

**Register custom Loader implementations in Owner by implementing the `Loader` interface and calling `ConfigFactory.registerLoader()` before creating any configuration instances.**

The Owner configuration library (matteobaccan/owner) supports property files, XML, and system properties through a pluggable **loader** abstraction. To handle non-standard formats like YAML, TOML, or INI, you must register custom Loader implementations that teach Owner how to parse these sources. This guide walks through the exact steps based on the current source code implementation.

## Understanding the Loader Architecture

Owner delegates all resource loading to classes implementing the **`Loader`** interface defined in [`src/main/java/org/aeonbits/owner/loaders/Loader.java`](https://github.com/matteobaccan/owner/blob/main/src/main/java/org/aeonbits/owner/loaders/Loader.java). A valid loader must provide three operations: decide if it can handle a URI via `accept(URI)`, read the resource into a `Properties` object via `load(Properties result, URI uri)`, and generate a default URI specification via `defaultSpecFor(String uriPrefix)`.

The **`LoadersManager`** class in [`src/main/java/org/aeonbits/owner/LoadersManager.java`](https://github.com/matteobaccan/owner/blob/main/src/main/java/org/aeonbits/owner/LoadersManager.java) maintains an ordered list of registered loaders. By default, it instantiates with `PropertiesLoader`, `XMLLoader`, and `SystemLoader` in its constructor. When Owner needs to load a resource, it iterates this list and selects the first loader whose `accept()` method returns true.

Registration occurs through the **`ConfigFactory`** façade ([`src/main/java/org/aeonbits/owner/ConfigFactory.java`](https://github.com/matteobaccan/owner/blob/main/src/main/java/org/aeonbits/owner/ConfigFactory.java)), which forwards calls to the singleton `Factory` instance. According to the source, newly registered loaders are inserted at **index 0** of the internal list (`loaders.add(0, loader)`), giving them priority over built-in loaders.

## Implementing the Loader Interface

Create a class implementing `org.aeonbits.owner.loaders.Loader` to define how Owner should parse your custom format. The implementation must handle URI acceptance, content parsing, and default specification generation.

```java
package org.example.owner.loaders;

import org.aeonbits.owner.loaders.Loader;
import java.io.IOException;
import java.net.URI;
import java.util.Properties;

public class YamlLoader implements Loader {

    @Override
    public boolean accept(URI uri) {
        String path = uri.toString().toLowerCase();
        return path.endsWith(".yaml") || path.endsWith(".yml");
    }

    @Override
    public void load(Properties result, URI uri) throws IOException {
        // Parse YAML content and populate result
        // Example: result.putAll(yamlParser.load(uri.toURL()));
        result.setProperty("yaml.loaded", "true");
    }

    @Override
    public String defaultSpecFor(String uriPrefix) {
        return uriPrefix + ".yaml";
    }
}

```

**Key implementation details:**

- The **`accept`** method should perform fast checks (typically file extensions) to avoid I/O overhead during loader selection.
- The **`load`** method receives an empty `Properties` instance that it must populate; it should throw `IOException` for any parsing or network errors.
- The **`defaultSpecFor`** method enables shorthand `@Source` annotations by converting a prefix like `"appConfig"` into a full URI like `"appConfig.yaml"`.

## Registering Your Custom Loader

You must register custom loaders **before** creating any configuration instances. Use the static `ConfigFactory.registerLoader()` method for global registration, or instantiate a new `Factory` for isolated loader sets.

### Global Registration via ConfigFactory

```java
import org.aeonbits.owner.ConfigFactory;
import org.example.owner.loaders.YamlLoader;

public class Application {
    public static void main(String[] args) {
        ConfigFactory.registerLoader(new YamlLoader());
        
        // Now YAML files are supported:
        MyConfig config = ConfigFactory.create(MyConfig.class);
    }
}

```

### Instance-Level Registration

For tests or multi-tenant scenarios requiring isolated loaders, use `ConfigFactory.newInstance()` to obtain a fresh `Factory` instance:

```java
Factory factory = ConfigFactory.newInstance();
factory.registerLoader(new YamlLoader());
MyConfig config = factory.create(MyConfig.class);

```

According to [`src/main/java/org/aeonbits/owner/ConfigFactory.java`](https://github.com/matteobaccan/owner/blob/main/src/main/java/org/aeonbits/owner/ConfigFactory.java) lines 40-42, passing `null` to `registerLoader` throws a `NullPointerException`, so always provide a valid instance.

## Priority and Thread-Safety Considerations

**Registration Order Matters.** Because `LoadersManager` adds new loaders at the head of the internal list, your custom implementation takes precedence over built-in loaders with overlapping `accept` patterns. If you need to extend rather than override behavior, ensure your `accept` logic is more specific than the default loaders.

**Thread Safety.** The `LoadersManager` uses a `ReentrantReadWriteLock` to protect the loader list. While registration is thread-safe, you should register loaders **once during application bootstrap** to minimize lock contention. Runtime registration after configs are instantiated is possible but not recommended for performance-critical paths.

## Summary

- Implement the **`Loader`** interface from `org.aeonbits.owner.loaders` to define parsing logic for new file formats.
- Register implementations via **`ConfigFactory.registerLoader()`** before creating any configuration objects to ensure they are available in the `LoadersManager` list.
- New loaders are inserted at the **front of the list**, giving them priority over default loaders like `PropertiesLoader` and `XMLLoader`.
- Use **instance-level `Factory` objects** via `ConfigFactory.newInstance()` when you need isolated loader configurations for testing or specific contexts.
- Avoid `null` registrations and perform registration during bootstrap to ensure thread-safe, contention-free initialization.

## Frequently Asked Questions

### How do I register a custom Loader for YAML files in Owner?

Implement the `Loader` interface with an `accept` method checking for `.yaml` or `.yml` extensions, then call `ConfigFactory.registerLoader(new YourYamlLoader())` before any `ConfigFactory.create()` calls. Owner will automatically use your loader when encountering matching URIs.

### What happens if two Loaders accept the same URI pattern?

Owner selects the **first** loader in the internal list whose `accept()` returns true. Since `LoadersManager` adds new registrations at index 0, custom loaders override built-in ones. Ensure your `accept` logic is specific enough if you want to avoid shadowing default behavior.

### Can I register Loaders after creating configuration instances?

Technically yes, but it is not recommended. The `LoadersManager` uses a `ReentrantReadWriteLock` making runtime registration thread-safe, but doing so after configs are instantiated can lead to inconsistent behavior or unnecessary lock contention. Register all loaders during application startup.

### What is the difference between ConfigFactory.registerLoader and Factory.registerLoader?

`ConfigFactory.registerLoader()` is a static convenience method that delegates to the singleton `Factory` instance, affecting all subsequent `ConfigFactory.create()` calls. `Factory.registerLoader()` works on specific `Factory` instances created via `ConfigFactory.newInstance()`, allowing isolated loader configurations for specific use cases or tests.