How do you exclude specific dependencies from a Spring Boot executable jar, and why would you?
answer
- <excludes>/<excludeGroupIds>/<excludeArtifactIds> in plugin config
- filters BOOT-INF/lib only, not compile classpath
- provided scope vs plugin exclude vs dependency <exclusions>
- Gradle: shape runtimeClasspath / compileOnly / developmentOnly
- devtools already excluded by default
basics
~10 sIn the spring-boot-maven-plugin configuration use <excludes> (by groupId/artifactId), <excludeGroupIds>, or <excludeArtifactIds> so those jars aren't nested into BOOT-INF/lib. You do this when a dependency is provided by the runtime environment or shouldn't be shipped.
solid answer
~40 sThe `repackage` goal has exclusion options that keep chosen dependencies *out of the packaged jar* without changing your normal compile classpath. In Maven you add to the plugin `<configuration>`: `<excludes>` listing `<exclude>` entries by `groupId`/`artifactId`, or the coarser `<excludeGroupIds>` / `<excludeArtifactIds>` comma lists. Excluded jars simply don't get copied into `BOOT-INF/lib`. Typical reasons: the dependency is supplied by the deployment target (an app server, an agent, a provided driver), it's dev-only tooling you don't want in production, or you're trimming image size. Note this is a *packaging* filter, distinct from Maven `<scope>provided</scope>` (which affects the whole classpath) and from a dependency `<exclusions>` block (which removes a transitive dependency from resolution entirely). Gradle handles the same need via configuration-level exclusions or by shaping the `bootJar` classpath, since bootJar bundles the `runtimeClasspath`.
code
java · 16 lines// Maven: keep chosen deps OUT of BOOT-INF/lib in the executable jar.
//
// <plugin>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-maven-plugin</artifactId>
// <configuration>
// <excludes>
// <exclude>
// <groupId>com.example</groupId>
// <artifactId>provided-by-server</artifactId>
// </exclude>
// </excludes>
// <excludeGroupIds>org.internal.tooling</excludeGroupIds>
// </configuration>
// </plugin>
public class ExcludeNotes { }go deeper
Aware that dependencies can be excluded from the jar via plugin config.
Knows the excludes/excludeGroupIds/excludeArtifactIds syntax.
Must distinguish plugin excludes vs provided scope vs dependency <exclusions> and pick correctly.
Weighs image size, attack surface, and environment-provided libs when defining packaging policy.
**What 'exclude' means here.** By default `repackage`/`bootJar` nests *every runtime dependency* into `BOOT-INF/lib`. The plugin's exclude options let you remove specific artifacts from that nested set while leaving your compile/test classpath untouched. It's purely a packaging-time filter over what gets copied into the fat jar. **Maven syntax.** Inside `<plugin><configuration>`: - `<excludes><exclude><groupId>..</groupId><artifactId>..</artifactId></exclude></excludes>` — precise, per-artifact. - `<excludeGroupIds>com.example,org.foo</excludeGroupIds>` — exclude everything from those groups. - `<excludeArtifactIds>some-lib</excludeArtifactIds>` — by artifactId. There are matching `<includes>` if you'd rather whitelist. These apply to the `repackage` goal (and, for the run goals, similar filtering exists). **Why exclude a dependency.** 1. *Provided by the environment.* If you deploy into a container/app server or run with a Java agent that already supplies a library, bundling it too can cause version conflicts or duplicate classes. 2. *Dev/tooling only.* Something needed to build or test but harmful/pointless in production. 3. *Licensing / distribution.* You must not redistribute a particular jar inside your artifact. 4. *Size / attack surface.* Trimming an unused-at-runtime library from the image. **Contrast with the alternatives — a key senior distinction.** - Maven `<scope>provided</scope>` (or Gradle `compileOnly`/`providedRuntime`): the dependency is on the *compile* classpath but excluded from the *runtime* packaging — semantically 'the environment provides it'. Spring Boot's plugin already omits `provided`-scoped deps from the fat jar in many cases, and `providedRuntime` in Gradle is designed exactly for war deployment. Use scope when the dependency is genuinely environment-provided everywhere. - Dependency `<exclusions>` inside a `<dependency>`: removes a *transitive* dependency from resolution entirely — it won't be on any classpath. Use when you want a different/no version of a transitive lib. - Plugin `<excludes>`: keeps the dependency in your build/classpath but strips it from the packaged jar only. Choosing the wrong mechanism is a common mistake: e.g. using plugin excludes when you actually wanted the library off the compile classpath, or vice versa. **Gradle side.** `bootJar` bundles the `runtimeClasspath` configuration, so you shape what's packaged by shaping that configuration: `configurations { runtimeClasspath { exclude group: 'x', module: 'y' } }`, or mark deps `compileOnly`/`developmentOnly` (devtools uses `developmentOnly`, which is excluded from the fat jar by default). There isn't a one-to-one `excludeGroupIds` on bootJar; you operate at the configuration level. **Gotchas.** - Excluding a jar the app actually needs at runtime yields `ClassNotFoundException` only when that code path runs — test the packaged jar, not just `bootRun`. - `spring-boot-devtools` and `spring-boot-configuration-processor` are excluded from the fat jar by default (via `developmentOnly`/`optional` handling) — you don't need manual excludes for those. - Plugin excludes don't change dependency *resolution*, so they can't fix a version-conflict on the compile classpath; use `<exclusions>` for that.
- What's the difference between the plugin's `<excludes>` and a `<scope>provided</scope>` dependency?`<scope>provided</scope>` marks a dependency as supplied by the runtime environment — it's on the compile classpath but Boot omits it from the fat jar. Plugin `<excludes>` is purely a packaging filter over `BOOT-INF/lib` and doesn't change dependency scope or the compile classpath.
- You excluded a library and `bootRun` still works but `java -jar` throws ClassNotFoundException. Why?`bootRun` uses the full project runtime classpath, so the excluded jar is still present. The exclude only affects the packaged jar, so the class is missing there. Always test the repackaged artifact, and either stop excluding it or ensure the environment provides it.
saying these in an interview costs you the question
- Thinking plugin <excludes> changes dependency resolution or the compile classpath
- Confusing plugin excludes with a <dependency><exclusions> block
- Expecting excludes to fix a compile-time version conflict