Sitemap

Demystifying Maven Build Essentials

7 min readJul 28, 2025

--

Building modern Java applications involves more than just writing code — you need to manage dependencies, configure build steps and sometimes even generate server‑ and client‑side code from API contracts.

This article explores key elements of the Java build ecosystem: the Maven Project Object Model (POM), the local .m2 repository, build lifecycles and dependency scopes. Together they form the backbone of reproducible builds.

The Maven POM (Project Object Model)

Maven uses an XML file named pom.xml to describe how a project should be built. The POM is the fundamental unit of work in Maven: when you run a Maven goal, the tool reads the POM, retrieves configuration information and executes the requested phase or plugin. The POM contains default values for common build locations (e.g., src/main/java for sources and target for build output) and defines elements such as:

  • Project coordinates — groupId, artifactId and version uniquely identify an artifact. Maven combines them into a fully qualified artifact name in the form groupId:artifactId:version. If packaging is not specified, Maven assumes a JAR.
  • Dependencies — Libraries your project needs. You list each dependency with its coordinates and optional scope. Maven handles transitive dependencies (dependencies of dependencies) automatically.
  • Plugins — Tasks executed during the build (e.g., compiler, test runners, code generators). Plugins themselves can have configurations and execution goals.
  • Build profiles — custom configurations that activate based on environment or command‑line flags.

A POM may inherit from a parent POM, gaining shared configuration such as dependency versions or plugin settings. This inheritance is defined via a <parent> element, and modules can inherit the groupId and version from the parent by omitting them in the child POM. You can also aggregate multiple modules by setting the parent’s packaging to pom and listing <module> entries.

At minimum a POM must contain the following elements:

<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
</project>

Maven will fill in other details (e.g., default packaging, repositories) using the Super POM and conventions. In a multi‑module build, you create a parent POM with pom packaging and list child modules; each module can override or extend inherited settings.

The Super POM is the default POM that all Maven projects inherit implicitly if you don’t specify certain elements in your own pom.xml.

Think of it as the parent-of-all Maven POMs. It provides sensible defaults so you don’t have to define every detail manually.

It’s defined in Maven’s internal files (maven-model-builder) and applies to every Maven project, unless you override it.

If you don’t specify a <parent> in your pom.xml, you still get inherited behavior from the Super POM.

A Parent POM is a Maven POM file that is explicitly inherited by one or more child projects.

Get Naveen’s stories in your inbox

Join Medium for free to get updates from this writer.

Example:- Imagine you have 5 microservices or Java libraries. Rather than repeating the same compiler settings, test framework versions, and repo URLs, you define them in a Parent POM, and each service just declares.

<parent>
<groupId>com.mycompany</groupId>
<artifactId>build-standards</artifactId>
<version>2.3.1</version>
</parent>
Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size
// Example

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<modules>
<module>module-a</module>
<module>module-b</module>
</modules>

<name>My Application</name>
<description>A simple Maven project example</description>

<dependencies>
<!-- Example dependency: JUnit for testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Maven Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
// module-a/pom.xml
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>module-a</artifactId>
</project>

The .m2 Repository

When Maven downloads dependencies and plugins, it caches them in a local repository under your home directory (${user.home}/.m2/repository by default). This cache allows Maven to avoid downloading the same artifacts repeatedly. You can customize the location of this repository by specifying a <localRepository> element in your user settings.xml file. The settings.xml file lives either in ${maven.home}/conf (global settings) or ${user.home}/.m2 (user‑specific settings), and its values are merged with user settings taking precedence.

A project’s pom.xml always points to remote repositories defined in the Super POM or in the project itself. When you declare a dependency, Maven first checks the local .m2 repository; if the artifact is missing, it downloads it from the specified remote repository (by default, the Central Repository at https://repo.maven.apache.org/maven2). The .m2/repository directory is therefore critical for builds – deleting it forces Maven to download all dependencies again, which is occasionally useful when clearing corrupted caches. The local repository also stores artifacts you install via mvn install, making them available for other projects on the same machine.

Maven Build Lifecycle and Dependency Scopes

Maven automates builds using lifecycles. The default lifecycle handles compiling, testing, packaging and deploying; the clean lifecycle removes previous build artifacts; the site lifecycle generates documentation.

Executing a phase triggers all preceding phases, e.g., running mvn package will run validate, compile, test and package. The main phases of the default lifecycle are:-

Press enter or click to view image in full size
// Run one of phase using below. This automatically runs all preceding phases (validate → install).
mvn install
// Other phases 
- mvn clean
pre-clean, clean, post-clean - Deletes previously built files like target/

- mvn site
pre-site, site, post-site, site-deploy - Generates and publishes site documentation

Dependencies in Maven have scopes that control where and when they are used. The Maven dependency mechanism defines six scopes :-

Press enter or click to view image in full size
// Each of these below phases trigger different secitons of POM file

validate → compile → test → package → verify → install → deploy
// Where does each section of POM file come in to play in maven phase 
| Section | Comes into Play | Description |
| ---------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`<dependencies>`** | During `compile`, `test`, `package`, `install`, etc. | Maven resolves and downloads dependencies **before** any relevant phase begins. Example: `JUnit` in `<scope>test</scope>` is used only during `mvn test`. |
| **`<build>`** | During `compile`, `package`, etc. | Defines build-specific settings like where compiled files go or which plugins to run. |
| **`<plugins>`** (inside `<build>`) | Any relevant lifecycle phase like `compile`, `package`, `install` | These are tied to lifecycle phases. E.g., `maven-compiler-plugin` runs during `compile`, and `maven-jar-plugin` runs during `package`. |
| **`<profiles>`** | When a profile is activated (e.g., `-Pdev`) | You define environment-specific settings. Used to conditionally override dependencies, plugins, properties. |
| **`<properties>`** | Resolved during entire build | Acts as variables usable across the POM, useful for reusability and version management. |
| **`<repositories>`** | During dependency resolution | If Maven needs to fetch a dependency from a custom repository instead of Maven Central. |
| **`<reporting>`** | When running reporting goals like `mvn site` | Used for generating documentation and reports like JUnit test reports or code coverage. |
| **`<modules>`** | When building a **multi-module** project | Specifies submodules that are part of the overall project. Triggered when building the parent project. |
// Types of file types in Java and their usage

| Type | Stands For | Used For | Contains |
| --------------- | ----------------------- | ---------------------------------------------------------- | -------------------------------------------------------- |
| **JAR** | Java ARchive | Standard Java applications, libraries | `.class` files, `META-INF`, config files, libraries |
| **WAR** | Web Application ARchive | Web applications (Servlets, JSPs, etc.) | Everything in a JAR **plus** `WEB-INF`, `web.xml`, JSPs |
| **EAR** | Enterprise ARchive | Enterprise apps using EJBs or combining multiple WARs/JARs | WARs + JARs + EJBs + application.xml |
| **APK** | Android Package | Android applications | Compiled Java/Kotlin code, resources, manifest, XML |
| **AAR** | Android ARchive | Android library packages | Similar to JARs but includes Android-specific components |
| **RSP** | Response File | (Rare) Used with some tools for automation | List of command-line arguments |
| **ZIP**/**TAR** | General archive | Used for packaging anything | Not Java-specific but used sometimes |
| **POM** | Project Object Model | Maven project metadata | XML format describing the project |

Understanding scopes helps you avoid bloated classpaths and ensures that production code does not depend on test libraries. Maven also supports dependency management sections for centralizing version numbers across multiple modules.

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.27</version>
<scope>compile</scope> <!-- Or runtime, test, etc. -->
</dependency>

Besides these, below files are generated during build process:-

Press enter or click to view image in full size

Conclusion

Understanding the structure of a Maven POM, the purpose of the .m2 repository, the build lifecycle and dependency scopes is essential for managing Java projects effectively. Armed with these tools, intermediate Java developers can build reproducible, modular applications with confidence.

--

--