How does the maven-war-plugin assemble a war, and how do dependency scopes affect what ends up inside it?
answer
- WEB-INF/classes + WEB-INF/lib + webapp root
- compile/runtime bundled, provided/test excluded
- servlet-api = provided
- failOnMissingWebXml=false for Servlet 3+
- overlays merge wars
basics
~10 sThe war plugin copies your classes to WEB-INF/classes and your compile/runtime dependencies to WEB-INF/lib, plus web resources at the root. provided and test scoped dependencies are left out so the container supplies them.
solid answer
~40 smaven-war-plugin runs during the package phase for war-packaged projects. It assembles WEB-INF/classes from compiled output, copies runtime classpath dependencies into WEB-INF/lib, includes src/main/webapp content at the archive root, and generates the manifest. Scope drives inclusion: compile and runtime dependencies are packaged into WEB-INF/lib; provided (e.g., servlet-api, the container's own libs) and test dependencies are excluded because the servlet container already provides them at runtime. You configure it via packagingExcludes/Includes, webResources for filtering, failOnMissingWebXml, and overlays to merge another war's content. A classic bug is bundling a provided API jar (or omitting provided so it leaks in), causing ClassCastException or LinkageError from duplicate classes loaded by different classloaders.
code
xml · 6 lines<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>go deeper
Know that classes go to WEB-INF/classes and dependency jars to WEB-INF/lib.
Explain how compile/runtime vs provided/test scopes change packaging.
Reason about classloader conflicts, overlays, and failOnMissingWebXml trade-offs.
Govern scope discipline across many war modules and decide overlay vs shared-jar strategies for common web assets.
## The war plugin's job For a `war`-packaged project, the `maven-war-plugin`'s `war:war` goal is bound to the `package` phase. It builds the archive in this layout: - `WEB-INF/classes/` — your compiled classes and `src/main/resources` content. - `WEB-INF/lib/` — dependency **jars** on the runtime classpath. - `/` (root) — everything under `src/main/webapp` (HTML, JSP, CSS, `WEB-INF/web.xml`). - `META-INF/MANIFEST.MF` — the generated manifest. ## Scope decides inclusion Maven dependency **scope** controls both compilation visibility and packaging: - **compile** (default) and **runtime** → packaged into `WEB-INF/lib`. - **provided** → on the compile classpath but **NOT packaged**; the runtime (servlet container) provides it. The Servlet API is the canonical example. - **test** → not packaged at all. This is why you declare `jakarta.servlet-api` as `provided`: Tomcat already ships it. Bundling it causes duplicate classes loaded by separate classloaders and runtime `LinkageError`/`ClassCastException`. ## Useful configuration - `failOnMissingWebXml` — older versions failed without a `web.xml`; for annotation-driven Servlet 3.0+ apps set it `false`. - `webResources` + `filtering` — inject build properties into web resources. - `packagingExcludes` / `packagingIncludes` — trim files from the final war. - **overlays** — merge the contents of another war dependency into this one (sharing common web assets across wars). ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.4.0</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> <packagingExcludes>WEB-INF/lib/commons-logging-*.jar</packagingExcludes> </configuration> </plugin> ``` Declare the container-provided API correctly: ```xml <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency> ```
- Why declare the servlet API as provided rather than compile?The container already supplies it at runtime. Bundling it in WEB-INF/lib creates duplicate classes loaded by different classloaders, causing LinkageError/ClassCastException.
- What is a war overlay?A mechanism to merge the contents (web resources and classes) of another war dependency into the current war, letting multiple wars share common assets.
saying these in an interview costs you the question
- Putting servlet-api at compile scope
- Believing test-scoped deps are bundled in the war
- Not knowing WEB-INF/classes vs WEB-INF/lib distinction