---
title: 'Configuration Sources'
source: 'https://academia.sh/en/courses/enterprise-application-framework/configuration-sources'
course: 'The Enterprise Application Framework'
language: en
updated: '2026-09-13T05:08:56+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Measures the precedence order among three configuration sources by running it across four separate settings'
    - 'Counts which source each setting won from and shows that the winning source never appears in the source at all'
    - 'Proves by running it that a misspelled setting name falls to the embedded default with no error at all'
---

# Configuration Sources

A setting value comes from one of three sources, and the precedence order is the IoC container's own rule — it never appears anywhere in the source: a value given from outside overrides a file source, and a file source overrides an embedded default. A misspelled setting name never disturbs the container at all — the program silently falls to the embedded default and reads empty instead of full.

The previous lesson showed that `prepare()` filled the `status` value by reading the `startup`
field, but it never asked where that field itself came from — it only assumed it was already
filled by the time of initialization. The line
`@Container.Value(name = "store.startup", defaultValue = "empty")` says two things: this field
may have a value from outside, and if it is not given, `"empty"` is used. But "a value given from
outside" does not come from a single place. This lesson splits that source into three and
measures the precedence order among them.

The third lesson had split a managed component's construction into three steps: constructor,
filling the value fields, init method — and the order of these three steps was the container's
own line order, not a number written in the source. This lesson takes the same pattern one level
deeper: filling the value fields is not a single step in itself, it is querying **three separate
sources** with their own order. The previous lesson's "which step first" question turns here into
"which source first," and the answer is in the same place again: in the container's own code, not
in the source.

## Three Sources, One Value

A setting value can come from one of three places: **a value given from outside** (supplied
directly by the party running the program, for example a command-line argument or a runtime
flag), **a file source** (a configuration set read while the container is built, carrying more
than one setting at once), and an **embedded default** (the annotation's own `defaultValue()`
value, the last resort placed by the class's author). All three can exist at the same time, and
the container must choose a single winner for every setting.

Choosing the winner is a chain: the external source is checked first, if it is not there the
file is checked, if it is not there either it falls to the embedded default. This chain's order
is the **container's own rule**; no setting declaration says "check the external source first" —
this behavior comes from the order of the `fill` method's code lines:
`if (externalSource.containsKey(...)) ... else if (fileSource.containsKey(...)) ... else ...`.
If this order were reversed (file checked first, then the external source), the same three
sources would produce a different winner — no setting definition in the source can change this
order, because the order lives inside the container's own code, not the setting definitions.

This ordering has its own logic, but that logic, too, is nowhere in the source: the external
source gets the highest priority because the party running the program may want to give a
different value every single time — so the same class, unchanged, can work with two different
values across two separate runs. The file source is one step below it, because it belongs to
**the whole** of a setup, not specific to a single run. The embedded default is at the bottom,
because it is the last resort saying "if nothing is given, use this," from the person who wrote
the class. All three steps rest on a reasonable justification, but the justification that orders
the three does not stand in the source even as a comment — it is embedded only in the `fill`
method's if-else order.

The `containsKey` check itself also carries a detail: even if a source gave an **empty string**
as a value for a key, that source is still considered the winner, because the check asks not for
a `null` check but for the key's **existence**. There is no such case in this measurement — none
of the four settings is defined with an empty string — but the mechanism itself makes this
distinction: "value is empty" and "value was never given" are two separate states, and `fill`
only moves to the next source in the second case.

- **CC16.** The precedence chain is a fixed three steps: external source, file, embedded
  default. This measurement does not add a fourth source (an environment variable, for example).
- **CC17.** The external source and the file are not read from a real file system or command
  line; both are `Map<String, String>` objects held in memory.
- **CC18.** Which source a setting comes from is determined only by the `containsKey` check; even
  if the key's value is an empty string, that source is still considered a "winner."
- **CC19.** Four settings represent three separate scenarios: one that is in both the external
  source and the file, one that is only in the file (two settings), one that is in neither.
- **CC20.** The winning source count is kept in a map; the counter starts at zero and only
  counts the four settings this measurement covers.
- **CC21.** There is no duration, file path, or real command-line argument in this measurement;
  the only thing carried is a setting name and a string value.

```java
// Configuration.java — precedence order among three configuration sources: external source, file source, embedded default
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Configuration {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface Value { String name(); String defaultValue(); }

    static final Map<String, Integer> winnerCount = new LinkedHashMap<>();
    static void won(String source) { winnerCount.merge(source, 1, Integer::sum); }

    final Map<String, String> externalSource;
    final Map<String, String> fileSource;

    Configuration(Map<String, String> externalSource, Map<String, String> fileSource) {
        this.externalSource = externalSource;
        this.fileSource = fileSource;
    }

    void fill(Object instance) throws Exception {
        for (Field f : instance.getClass().getDeclaredFields()) {
            Value v = f.getAnnotation(Value.class);
            if (v == null) continue;
            f.setAccessible(true);
            String value; String source;
            if (externalSource.containsKey(v.name())) { value = externalSource.get(v.name()); source = "external-source"; }
            else if (fileSource.containsKey(v.name())) { value = fileSource.get(v.name()); source = "file"; }
            else { value = v.defaultValue(); source = "embedded-default"; }
            f.set(instance, value);
            won(source);
            System.out.printf("%-24s %-6s winner: %s%n", v.name(), value, source);
        }
    }

    public static void main(String[] args) throws Exception {
        Map<String, String> external = new LinkedHashMap<>();
        external.put("store.connection-pool", "50");

        Map<String, String> file = new LinkedHashMap<>();
        file.put("store.connection-pool", "10");
        file.put("store.timeout", "30");
        file.put("store.startup.status", "full");

        Configuration c = new Configuration(external, file);
        ConfiguredComponent b = new ConfiguredComponent();

        System.out.println("-- which source each setting came from --");
        c.fill(b);

        System.out.println();
        System.out.println("-- winning source count --");
        winnerCount.forEach((k, v) -> System.out.printf("%-20s %d%n", k, v));

        System.out.println();
        System.out.println("values read:");
        System.out.println("pool size        : " + b.poolSize);
        System.out.println("timeout          : " + b.timeout);
        System.out.println("startup status   : " + b.startupStatus);
        System.out.println("cache size       : " + b.cacheSize);
    }
}

class ConfiguredComponent {
    @Configuration.Value(name = "store.connection-pool", defaultValue = "5") String poolSize;
    @Configuration.Value(name = "store.timeout", defaultValue = "10") String timeout;
    @Configuration.Value(name = "store.startup.status", defaultValue = "empty") String startupStatus;
    @Configuration.Value(name = "store.cache.size", defaultValue = "100") String cacheSize;
}
```

```
-- which source each setting came from --
store.connection-pool    50     winner: external-source
store.timeout            30     winner: file
store.startup.status     full   winner: file
store.cache.size         100    winner: embedded-default

-- winning source count --
external-source      1
file                 2
embedded-default     1

values read:
pool size        : 50
timeout          : 30
startup status   : full
cache size       : 100
```

## Four Settings, Four Different Winners

`store.connection-pool` is defined both in the external source (`50`) and in the file (`10`), and
the winner is the external source — the chain's first link. This shows that how many places a
setting is defined in does not matter: the only thing that determines the winner is which source
is queried earlier in the chain. `store.timeout` and `store.startup.status` exist only in the
file, not in the external source; both win from the file because the first check
(`externalSource.containsKey`) returns `false` and moves to the second check.
`store.cache.size` is in neither source — it falls to the chain's third link, the embedded
default.

`winnerCount` is kept on a `LinkedHashMap`, not a `HashMap` — even though correct counting is the
only thing this lesson needs, `LinkedHashMap` preserves insertion order, so the output prints in
the same order on every run too. Even this small choice is a reminder: the container's own
internal data structure choices affect the readability of the output, even when they do not
affect the measured behavior — and this choice, too, appears in none of the setting declarations
in the source.

The count line summarizes this: one of four settings came from the external source, two from the
file, one from the embedded default. This number is **the measure of the precedence order's
effect** — it shows how many settings depend on which source "winning." The Infrastructure as
Code course measured whether a value given from outside is recorded or not; this lesson takes the
same "value given from outside" idea but asks a different question — not recording, but which
component actually **sees** that value.

The `f.setAccessible(true)` call appears here again too, because `ConfiguredComponent`'s fields
are visible at package level and the `fill` method reaches these fields through a general
`Object` — the same access check bypass seen in the second lesson's `Service` constructor, this
time for fields. The difference: here only a value is written, no method is called.

The setting name itself is also not a type, only a string — `String name()`. This is a choice
from the same class as the third lesson's scope name observation: the compiler cannot check
whether a setting name really corresponds to an existing source key, because all it has is a
character sequence. `"store.connection-pool"` and `"store.connection_pool"` (underscore instead
of hyphen) are two strings equally valid as far as the compiler is concerned, but two completely
separate keys as far as `containsKey` is concerned. This lesson uses correctly written names in
all four settings; the next section deliberately breaks this assumption.

## When the Order Changes, the Winner Changes

The previous section's claim — that the order is embedded in the container's own code — remains
an unproven assertion if left in plain prose. Running the same two sources, the same two values,
with only the query order reversed, confirms this claim:

```java
// ReversedOrder.java — same two sources, only the query order changed, and the winner changes too
import java.util.*;

public class ReversedOrder {
    static String filePriority(Map<String, String> externalSource, Map<String, String> file, String name, String defaultValue) {
        if (file.containsKey(name)) return file.get(name);
        if (externalSource.containsKey(name)) return externalSource.get(name);
        return defaultValue;
    }

    public static void main(String[] args) {
        Map<String, String> external = Map.of("store.connection-pool", "50");
        Map<String, String> file = Map.of("store.connection-pool", "10");

        System.out.println("winner when external source queried first : " + external.get("store.connection-pool"));
        System.out.println("winner when file queried first             : "
                + filePriority(external, file, "store.connection-pool", "5"));
    }
}
```

```
winner when external source queried first : 50
winner when file queried first             : 10
```

The content of the `external` and `file` maps never changed between these two lines — the key
`store.connection-pool` stands with the same values in both. The only thing that changed is which
map the `filePriority` method looks at **first**. `Configuration.fill` queries the external
source first, and `50` wins; the `filePriority` method written here queries the file first, and
`10` wins. The data in the sources is the same, the setting name is the same, the component
itself does not even exist in this measurement — only the order of two `if` lines inside a method
reversed, and the winner changed. This is direct proof that the precedence order depends not on
any setting declaration in the source, but only on the order of the code that writes the
container.

## Limiting Measurement: A Spelling Difference

The key `store.startup.status` sits in the file with the value `"full"`. A component wants to
read this value, but writes the key wrong in its annotation — a hyphen where a dot should be:

```java
// MisspelledSettingName.java — the value sitting in the file never wins at all, because of a typo in the setting name
public class MisspelledSettingName {
    public static void main(String[] args) throws Exception {
        java.util.Map<String, String> external = java.util.Map.of();
        java.util.Map<String, String> file = java.util.Map.of("store.startup.status", "full");

        Configuration c = new Configuration(external, file);
        Misspelled b = new Misspelled();
        c.fill(b);

        System.out.println("value sitting in the file : full");
        System.out.println("value the component read  : " + b.status);
    }
}

class Misspelled {
    @Configuration.Value(name = "store-startup.status", defaultValue = "empty") String status;
}
```

```
store-startup.status     empty  winner: embedded-default
value sitting in the file : full
value the component read  : empty
```

`Misspelled`'s `name` value is `"store-startup.status"` (a hyphen after "startup"), while the key
in the file is `"store.startup.status"` (a dot) — the two strings are completely different as far
as Java's `equals` comparison is concerned, `containsKey` returns `false` for both, and `fill`
falls to the chain's third link. There is no exception, no warning, no log line saying "key not
found" — the program silently reads `"empty"`; the `"full"` value sitting in the file is never
seen at all.

This is the exact opposite of the second lesson's unbound interface error: there, the container
threw an exception **immediately and loudly**; here, the container **makes no sound at all**.
Just as the precedence order stands nowhere in the source, a source "losing" leaves no trace in
the source either — the losing source is treated as if it never existed at all. The only way to
notice this defect is to compare, by hand, the value sitting in the file against the value the
component reads; the code itself never points out this difference.

It is worth looking at why this behavior does not change: the `fill` method carries no mechanism
that could notice a setting name "almost matching" in the file, because such a mechanism would
itself become a new source of defects — who decides which spelling difference is "acceptable"?
The container never asks this question at all, it only looks for an exact match. This is the
same design choice as the third lesson's unrecognized scope name: instead of making validation
early and automatic, leaving validation out entirely and showing the result only through
behavior. In both cases the defect is not an exception, it is an output that looks **different
from expected but valid** — in `WrongScope` it was two separate objects, here it is `"empty"`
instead of `"full"`.

These two observations — the winner changing when the order changes, no source winning at all
because of a spelling difference — are two faces of the same fact. In the first, all three
sources were defined with the correct name, and the order change determined which one showed up.
In the second, the order never changed at all, but because the setting name was misspelled, even
the chain's first two links could never be queried at all — the `containsKey` question was asked
of the wrong key. Both defects come from the same root: which value a setting will carry is a
result that cannot be read directly from any line in the source, it can only be extracted by
knowing the container's rule and what the sources contain at that moment.

## Summary

- A setting value can come from three sources: a value given from outside, a file source, and
  the annotation's embedded default; the chain that determines the winner is the container's own
  code order.
- A setting being defined in more than one source does not matter — only the source queried
  earlier in the chain wins.
- One of four settings came from the external source, two from the file, one from the embedded
  default; this distribution is the direct measure of the precedence order's effect.
- The precedence order itself stands nowhere in the source; knowing which source wins requires
  knowing the container's code.
- A misspelled setting name throws no error at all — the container silently falls to the
  embedded default, and the real value sitting in the file is never seen.

## Next Step

The fourth setting (`store.cache.size`) was not defined in any source and fell to the embedded
default — but this did not mean the container "did nothing," it only meant it reached the
chain's last link. In some cases, the container itself notices that a component was never
declared at all and brings in **its own** default — not a setting value, but the component
itself. The next lesson measures this different kind of default: what the container does when a
component is never written at all, and why this decision never shows up anywhere in the source
with a "conditional" word.
