skip to content

How does maven-resources-plugin work, and what does resource filtering do?

level: middleimportance: should knowfreq 45%

answer

  1. copies src/main/resources→target/classes
  2. filtering = ${...} token replace at build time
  3. binary files corrupt → nonFilteredFileExtensions
  4. Spring Boot uses @...@ delimiter
  5. process-resources / process-test-resources

basics

~10 s

maven-resources-plugin copies files from src/main/resources into target/classes. With filtering enabled, it replaces ${...} placeholders in those files with property values during the copy.

solid answer

~40 s

maven-resources-plugin runs at process-resources (goal `resources`) and process-test-resources (goal `testResources`), copying non-source files into `target/classes` / `target/test-classes` so they end up on the classpath / in the artifact. Its key feature is **filtering**: when a `<resource>` has `<filtering>true</filtering>`, the plugin substitutes `${property}` tokens with values from POM properties, `settings.xml`, system properties, or profile-activated values. A common use is injecting `${project.version}` or environment-specific config into `application.properties`. Pitfalls: filtering binary files can corrupt them (use `<nonFilteredFileExtensions>` or separate filtered/unfiltered resource roots), and Spring Boot remaps the default filter delimiter to `@...@` to avoid clashing with Spring's own `${...}` placeholders. Filtering happens at build time, not runtime.

code

xml · 14 lines
xml
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
      <excludes><exclude>**/*.p12</exclude></excludes>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>false</filtering>
      <includes><include>**/*.p12</include></includes>
    </resource>
  </resources>
</build>

go deeper

for a junior

Know it copies resources to target/classes so they end up in the jar.

for a middle

Explain filtering and the ${...} substitution at build time.

for a senior

Handle binary-file and delimiter pitfalls (Spring Boot @...@, nonFilteredFileExtensions).

for a principal

Design per-environment config strategy (filtering vs externalized config) and its security implications for secrets.

## What it does **maven-resources-plugin** moves your *non-compiled* files (config, templates, static assets) from the source tree into the build output so they are packaged into the artifact and visible on the classpath. - Goal `resources` → bound to **process-resources**: `src/main/resources` → `target/classes`. - Goal `testResources` → bound to **process-test-resources**: `src/test/resources` → `target/test-classes`. - Goal `copy-resources` → manual, copies an arbitrary set somewhere (often used in custom executions). ## Filtering **Filtering** = token replacement during the copy. With `<filtering>true</filtering>`, every `${...}` token in the copied files is replaced by a property value resolved at build time. Sources of values: - POM `<properties>` and built-ins like `${project.version}`, `${project.artifactId}`. - `-D` system properties and environment via `${env.HOME}`. - External property files via `<filters><filter>...properties</filter></filters>`. - Active **profiles** (commonly used for per-environment config). ```xml <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> ``` With `app.version=${project.version}` in `application.properties`, the packaged file gets the real version baked in. ## Common gotchas - **Binary corruption**: filtering images/keystores/PDFs can break them because the plugin reads them as text. Solution: keep binaries in a separate non-filtered `<resource>` root, or set `<nonFilteredFileExtensions>` on the plugin. - **Delimiter clashes**: Spring also uses `${...}` at runtime. Spring Boot's parent POM therefore configures the resources plugin to use `@...@` as the filter delimiter, so `@project.version@` is filtered at build time and `${...}` is left for Spring at runtime. - **Build-time only**: filtering is not dynamic; values are frozen when you build. ## Inspecting `mvn process-resources` then look in `target/classes` to confirm substitution.

  • Why does Spring Boot change the filtering delimiter to @...@?
    Because Spring uses ${...} for its own runtime property resolution. Using @...@ for Maven filtering lets build-time tokens (@project.version@) be replaced while leaving ${...} intact for Spring at runtime.
  • What can go wrong if you filter a keystore or image?
    The plugin processes it as text and can corrupt binary content. Exclude binaries from filtering via a non-filtered resource root or nonFilteredFileExtensions.

saying these in an interview costs you the question

  • Saying filtering happens at runtime — it is a build-time copy step.
  • Filtering all resources including binaries without exclusions.
  • Confusing resources:resources with the compiler — resources never compiles code.

context