
# Placeholders

A placeholder `${key}` is replaced by the value of the property `key`; `${key:default}` uses `default` when `key` is not set.

## Why

A fact should be written once. The shop's URL is set in one place, and the API address is built from it. A value that differs per machine comes from an environment variable, with a default for the laptop. Placeholders do both, without code.

## Syntax

| Form | Result |
|---|---|
| `${shop.name}` | The value of `shop.name`. Fails where the value is needed when no source has it. |
| `${shop.port:8080}` | `8080` when `shop.port` is not set. |
| `${shop.motto:}` | An empty string when `shop.motto` is not set. |
| `${a:${b:c}}` | A default can contain placeholders. |
| `${shop.${region}.url}` | A key can contain placeholders. |
| `\${not.a.placeholder}` | A literal `${not.a.placeholder}`. In `.properties`, escape the backslash: `\\${`. |
| `${random.int(10)}`, `${random.uuid}` | A new random value on every read. |

Rules:

- A value that is found is resolved again, so values can refer to other values. A loop is detected and reported.
- `${db:url}` is first looked up as the key `db:url`, then as `db` with the default `url`.
- A `${` without a closing `}` is kept as text.
- Relaxed names apply: `${SHOP_URL}` finds the environment variable, and `${shop.url}` finds `SHOP_URL` too.

## Example

```yaml
# lib/resources/application.yml
shop:
  name: NEON Books
  title: ${shop.name} (${spring.profiles.active:local})
  url: ${SHOP_URL:http://localhost:8080}
  api: ${shop.url}/api/v${shop.api-version:1}
  instance-id: ${random.uuid}
```

```dart
@Service()
class ApiClient {
  ApiClient(
    // A raw string: in '${...}' Dart would interpolate.
    @Value(r'${shop.api}') this.baseUrl,
    @Value(r'${shop.timeout:5s}') this.timeout,
  );

  final String baseUrl;
  final Duration timeout;
}
```

## Where they are resolved

| Place | When a placeholder cannot be resolved |
|---|---|
| Values in config files and other sources, when read through the environment | Fails where the value is read |
| `@Value` | Fails the start, naming the key. A nullable target gets `null`. |
| `environment.getProperty`, `getPropertyAs` | Throws `PlaceholderResolutionException` |
| `environment.resolvePlaceholders(text)` | Stays as written |
| `environment.resolveRequiredPlaceholders(text)` | Throws `PlaceholderResolutionException` |
| `@ConfigurationProperties` fields | Kept as written. `@RawValue()` skips resolution for one field. |
| `@PropertySource` locations | Fails the start. With `ignoreResourceNotFound: true` the location is skipped. |
| `spring.config.import` locations | Fails the start. An `optional:` import is skipped. |
| Starter annotations that take configurable text, such as `@Scheduled(fixedDelayString: ...)`, the URL of a Feign client and request mapping paths | Depends on the starter |
| `EmbeddedValueResolverAware` beans | The bean calls the resolver itself |

Placeholders are not SpEL. `#{...}` is evaluated only in `@Value` and `@ConditionalOnExpression`, never in config files. In `@Value`, placeholders are replaced first, so `#{${shop.max-basket-size} * 2}` works.

In Dart code, always write placeholders as raw strings: `@Value(r'${shop.name}')`. The `dart_spring` editor extension marks a plain string as an error.

## Resolving your own text

`PropertyPlaceholderHelper` is the engine behind the environment. Use it to fill placeholders from something that is not the configuration, such as template variables:

```dart
final helper = PropertyPlaceholderHelper();
final values = {'host': 'localhost', 'port': '8080'};
helper.replacePlaceholders(
  r'http://${host}:${port:80}/${path:}',
  (key) => values[key],
); // 'http://localhost:8080/'
```

## In Spring (Java)

```java
@Value("${shop.api}") String baseUrl;
@Value("${shop.timeout:5s}") Duration timeout;
```

The syntax and rules are Spring's, including the Spring 6.2 lookup of `${db:url}`. Differences that matter:

- In Dart, a placeholder in code must be in a raw string (`r'...'`).
- `${random.*}` returns at most 53-bit numbers on the web.

## See also

- [@Value](value.md)
- [Environment](environment.md)
- [Property sources](property-source.md)
- [@ConfigurationProperties](configuration-properties.md)
- [SpEL](../spel/index.md)
- [Aware interfaces](../beans/aware.md)
