What pitfalls and precedence rules govern ${} interpolation across the pom, resources, and the command line — and how would you debug an unresolved or wrong value?
answer
- -D > <properties> > built-ins
- filters feed filtering only
- no filtering => token stays literal
- Spring ${} collision -> @...@
- help:evaluate / effective-pom to debug
basics
~20 sInterpolation replaces ${name} from sources with a precedence order (roughly -D system props > pom properties > built-ins). Pitfalls: unfiltered resources keep tokens literally, ${} clashes with framework placeholders, and unknown tokens stay as-is. Debug with mvn help:evaluate and effective-pom.
solid answer
~40 s${} interpolation pulls values from multiple sources with a precedence order: command-line system properties (-D) and JVM system properties generally win, then pom <properties>, then built-in families (project.*, settings.*, env.*), with filter files feeding only resource filtering. Key pitfalls: (1) a token in a resource file is only replaced if that resource has filtering enabled — otherwise it is copied literally; (2) frameworks like Spring use ${} too, so filtering can wrongly consume them — fix via delimiters or by not filtering; (3) unknown properties are left as the literal ${name} rather than blanked or failing; (4) recursive/self-referential definitions can fail to resolve. To debug, I use mvn help:evaluate -Dexpression=... to see resolved values, mvn help:effective-pom to see the merged model, and check the filtered output under target/classes.
code
bash · 3 linesmvn help:evaluate -Dexpression=project.version -q -DforceStdout
mvn help:effective-pom -Doutput=effective-pom.xml
mvn package && cat target/classes/application.propertiesgo deeper
Knows ${} is replaced and that unresolved ones can remain literal.
Knows filtering must be on and the basic source list.
Reasons about precedence, the Spring collision, encoding, and debugs with help goals.
Sets org conventions for delimiters/encoding and fails-fast strategies so silent unresolved tokens never reach prod.
## How interpolation works When Maven builds the **effective model** and when the resources plugin filters files, it scans for `${name}` and substitutes a resolved value. If `name` cannot be resolved, the literal `${name}` is left in place (Maven does not error or blank it by default). ## Precedence (which source wins) For the same name, the effective order is roughly: 1. Command-line / JVM **system properties** (`-Dname=value`). 2. Pom `<properties>` (with child overriding inherited parent). 3. Built-in families: `project.*`, `settings.*`, `env.*`. 4. For resource filtering only: external `<filters>` files (later filter overrides earlier). So `mvn -Dspring.version=6.2 package` beats a `<spring.version>6.1</spring.version>` in the pom. ## Pitfall 1 — token only replaced if filtering is on A `${project.version}` inside `src/main/resources/app.properties` stays literal unless that `<resource>` has `<filtering>true</filtering>`. The most common 'why is my placeholder not replaced' cause. ## Pitfall 2 — framework `${}` collision Spring, Logback, etc. use `${}` for *their* runtime placeholders. With filtering on, Maven resolves them at build time — breaking the framework. Mitigate by changing Maven's delimiter (e.g. use `@prop@` in resources via the resources-plugin `<delimiters>` / `useDefaultDelimiters=false`), escaping, or isolating those files in a non-filtered directory. ## Pitfall 3 — unknown tokens stay literal A typo like `${projetc.version}` does not fail; it ships the literal text. Catch it by inspecting `target/classes`. ## Pitfall 4 — encoding Filtering rewrites files; set `project.build.sourceEncoding` so non-ASCII content isn't mangled. ## Debugging toolkit ```bash # resolve any expression against the effective model mvn help:evaluate -Dexpression=project.version -q -DforceStdout # dump the fully-merged pom (after inheritance + interpolation) mvn help:effective-pom # inspect the actually-filtered output ls target/classes && cat target/classes/application.properties ``` `help:evaluate` is the fastest way to confirm what a property resolves to; `help:effective-pom` shows how inheritance and property overrides merged.
- A ${...} in your properties file is shipping literally. First thing to check?Whether that resource directory has <filtering>true</filtering>; without it, tokens are copied verbatim.
- How do you stop Maven from resolving Spring's ${} placeholders during filtering?Change Maven's filtering delimiter (e.g. to @prop@) via the resources plugin, escape the token, or keep that file in a non-filtered directory.
- What happens to an unknown/typo'd property token during filtering?It is left as the literal ${typo} text — the build does not fail or blank it by default.
saying these in an interview costs you the question
- Saying an unresolved ${name} blanks out or fails the build.
- Forgetting that filtering must be enabled for resource tokens to resolve.
- Claiming pom <properties> always override -D system properties.