
# Property sources

A property source is one place property values come from, under a name: a config file, the command line, the environment variables, a map of defaults.

## Why

The environment holds no properties itself. It holds an ordered list of property sources and asks them in turn; for each key, the first source that has it wins. That order is how the command line overrides a config file, and a config file overrides the defaults.

An application rarely touches the list. It matters in two cases:

- to know which value wins when several places set the same key;
- to read properties from a place the framework does not know, such as a feature flag service or a secrets vault.

## The order of sources

From the highest:

| # | Source | Name in the list |
|---|---|---|
| 1 | `run(properties: ...)`, `springBootTest(properties: ...)` | |
| 2 | Command-line arguments | `commandLineArgs` |
| 3 | `--dart-define` values | `dartDefines` |
| 4 | `SPRING_APPLICATION_JSON` / `spring.application.json` | `spring.application.json` |
| 5 | OS environment variables (in Flutter apps only with `spring.flutter.scan-os-environment=true`) | `systemEnvironment` |
| 6 | `${random.*}` values | `random` |
| 7 | Config files: imported files above their importer, profile files above plain ones, `file:./` above `resources:/`, `.properties` above `.yml` above `.yaml`, a later document above an earlier one | one source per file and document |
| 8 | `@PropertySource` files | the annotation's `name`, or the location |
| 9 | `SpringApplication(defaultProperties: ...)` | `defaultProperties` |

A list property comes whole from the highest source that has it. Map entries are merged from every source, the higher source winning per key.

### Command line

```bash
dart run bin/main.dart --shop.max-basket-size=30 --spring.profiles.active=prod --debug
```

- `--key=value` sets `key`. Only the first `=` separates.
- `--flag` alone sets `flag` to an empty value.
- A repeated option keeps all its values, joined with commas.
- A lone `--` ends the options. Arguments that are not options are in `nonOptionArgs`.

### OS environment variables and `--dart-define`

`SHOP_MAXBASKETSIZE=20` sets `shop.max-basket-size`. The name is split at `_` and lower-cased, and dashes do not matter. A number part becomes an index: `SHOP_HOSTS_0=eu` sets `shop.hosts[0]`. `--dart-define` values use the same names.

A client app cannot keep secrets: `--dart-define` values and config files end up inside the APK, the IPA or `main.dart.js`.

## Example: a source of your own

Extend `AbstractPropertySource` and add the source to the environment early, from an `EnvironmentPostProcessor`, so every bean sees its values.

```dart
import 'package:spring_boot_dart/spring_boot_dart.dart';

// Flags from a flag service; they override the config files.
class FeatureFlagSource extends AbstractPropertySource<FlagClient> {
  FeatureFlagSource(FlagClient client) : super('featureFlags', client);

  @override
  Object? getProperty(String name) => source.valueOf(name);
}

class FeatureFlags implements EnvironmentPostProcessor {
  @override
  void postProcessEnvironment(
    ConfigurableEnvironment environment,
    SpringApplication application,
  ) {
    final sources = environment.propertySources;
    // Above the environment variables, below the command line.
    sources.addBefore(
      SystemEnvironmentPropertySource.defaultName,
      FeatureFlagSource(FlagClient()),
    );
    // A plain map, below everything.
    sources.addLast(MapPropertySource('fallbacks', {'shop.beta': 'false'}));
  }
}

final app = SpringApplication(
  [appBeans],
  environmentPostProcessors: [FeatureFlags()],
);
```

When the source can list its keys, extend `EnumerablePropertySource` instead. `@ConfigurationProperties` binding then finds keys by prefix, which lists and maps need.

For a file that belongs to one part of the application, `@PropertySource` is simpler:

```dart
@Configuration()
@PropertySource([
  'file:config/app.properties',
  r'file:config/app-${env:local}.properties',
], ignoreResourceNotFound: true)
class AppConfig {}
```

## Members

| Type | Member | Meaning |
|---|---|---|
| `AbstractPropertySource<T>` | `name` | Unique in the list. Two sources with the same name are equal. |
| | `source` | The object the values come from: a map, the arguments, a client. |
| | `getProperty(name)` | The raw value, or `null` when this source does not have it. |
| | `containsProperty(name)` | Whether the source has the key. |
| `EnumerablePropertySource<T>` | `propertyNames` | Every key of the source. |
| `MapPropertySource` | `MapPropertySource(name, map)` | A source backed by a `Map<String, Object?>`. |
| `CompositePropertySource` | `addPropertySource`, `addFirstPropertySource` | Several sources under one name. |
| `MutablePropertySources` | `addFirst(source)` | Wins over every other source. |
| | `addLast(source)` | Every other source wins over it. |
| | `addBefore(name, source)`, `addAfter(name, source)` | Next to a named source. Throw `ArgumentError` when the name is absent. |
| | `get(name)`, `containsSource(name)` | Find a source. |
| | `remove(name)`, `replace(name, source)` | Take out, or swap in place. |
| | `precedenceOf(source)` | 0 for the winning source, -1 when absent. |

Adding a source whose name is already present removes the old one first.

`@PropertySource` parameters:

| Name | Type | Default | Meaning |
|---|---|---|---|
| `value` (positional) | `List<String>` | required | Resource locations (`file:`, `resources:`, patterns). May contain `${...}`. The last location wins. |
| `name` | `String?` | `null` | The source's name. Derived from the location when `null`. |
| `ignoreResourceNotFound` | `bool` | `false` | Skip a missing file instead of failing the start. |
| `factory` | `Type?` | `null` | A `PropertySourceFactory` for another format, such as YAML. `.properties` when `null`. |
| `encoding` | `String?` | `null` | The charset of the files. UTF-8 when `null`. |

## In Spring (Java)

```java
class FeatureFlagSource extends PropertySource<FlagClient> {
  FeatureFlagSource(FlagClient client) { super("featureFlags", client); }

  @Override
  public Object getProperty(String name) { return getSource().valueOf(name); }
}

environment.getPropertySources().addBefore(
    StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
    new FeatureFlagSource(client));
```

Differences that matter:

- The base class is `AbstractPropertySource`, because `PropertySource` is the name of the annotation.
- There are no JVM system properties. `--dart-define` values take a place just below the command line.
- `EnvironmentPostProcessor`s are passed to `SpringApplication`, not listed in `META-INF/spring.factories`.

## See also

- [Environment](environment.md)
- [Profiles](profiles.md)
- [Placeholders](placeholders.md)
- [@Configuration](../beans/configuration.md)
- [Resource](../resources/resource.md)
