POM File
About
In Maven, the Project Object Model (pom.xml
) is the heart of our build system. It defines what our project is, how it should be built, what it depends on, and how it interacts with the Maven ecosystem.
While small projects may use a minimal pom.xml
, larger and enterprise projects often have highly structured POMs that handle:
Dependency management
Build configuration
Multi-module orchestration
Profiles for environment-specific builds
Understanding its structure means understanding how Maven thinks about projects and that’s the first step toward mastering Maven.
Sample POM File
<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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- ===== Model Version ===== -->
<modelVersion>4.0.0</modelVersion>
<!-- ===== Project Coordinates ===== -->
<groupId>com.example</groupId>
<artifactId>sample-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- ===== Project Metadata ===== -->
<name>Sample Maven Application</name>
<description>An example project demonstrating all POM sections</description>
<url>https://example.com/sample-app</url>
<!-- ===== Parent POM ===== -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/> <!-- Look up parent from repository -->
</parent>
<!-- ===== Properties ===== -->
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
</properties>
<!-- ===== Dependency Management ===== -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- ===== Dependencies ===== -->
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- ===== Build Configuration ===== -->
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<!-- Plugins -->
<plugins>
<!-- Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!-- Surefire Plugin (Unit Testing) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
<!-- JAR Plugin (Custom Manifest) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.MainApplication</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<!-- ===== Repositories ===== -->
<repositories>
<repository>
<id>central</id>
<name>Maven Central Repository</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<!-- ===== Plugin Repositories ===== -->
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Central Plugins</name>
<url>https://repo1.maven.org/maven2/</url>
</pluginRepository>
</pluginRepositories>
<!-- ===== Profiles ===== -->
<profiles>
<!-- Development Profile -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<maven.compiler.debug>true</maven.compiler.debug>
</properties>
</profile>
<!-- Production Profile -->
<profile>
<id>prod</id>
<properties>
<maven.compiler.debug>false</maven.compiler.debug>
</properties>
</profile>
</profiles>
<!-- ===== Modules (Multi-module projects) ===== -->
<modules>
<module>service-module</module>
<module>web-module</module>
</modules>
</project>
The <project>
Root Element
<project>
Root ElementIn Maven, every POM must start with a <project>
element.
This is the container for all other POM elements - think of it as the root of the Maven configuration tree.
From our sample file:
<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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
A. The xmlns
Attribute
xmlns
Attributexmlns="http://maven.apache.org/POM/4.0.0"
Defines the default XML namespace for Maven’s POM schema.
Ensures Maven interprets elements like
<groupId>
and<version>
according to POM 4.0.0 rules.Without this, Maven could throw parsing errors or misinterpret elements.
B. The xmlns:xsi
Attribute
xmlns:xsi
Attributexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
This namespace is not Maven-specific — it’s an XML Schema Instance namespace from W3C.
It enables the use of
xsi:schemaLocation
in XML files to tell tools where to find the schema definition.
C. The xsi:schemaLocation
Attribute
xsi:schemaLocation
Attributexsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd"
Maps the Maven namespace to the actual XSD file that describes POM structure.
The first part:
http://maven.apache.org/POM/4.0.0
— the namespace URI.The second part:
https://maven.apache.org/xsd/maven-4.0.0.xsd
— the location of the XML Schema Definition (XSD) for Maven POM.
When tools like IntelliJ or Eclipse read our pom.xml
, they use this schema to:
Validate if elements are correct
Show autocomplete suggestions
Catch typos or misplaced elements before build time
D. Closing the <project>
Tag
<project>
TagAfter defining namespaces, the <project>
tag is not closed immediately instead, it wraps all other POM sections until the very end:
</project>
E. Why This Section Matters ?
Without a proper
<project>
root with namespaces, Maven won’t recognize our file as a valid POM.Proper namespace/schema definitions ensure:
IDE support (syntax highlighting, code completion)
Validation before build
Compatibility with Maven 3.x and above
The <modelVersion>
Element
<modelVersion>
ElementFrom our sample:
<modelVersion>4.0.0</modelVersion>
A. Purpose
Defines which version of the Project Object Model (POM) we are using.
Ensures Maven knows how to interpret the structure and available elements in our POM.
B. Current Value
Always
4.0.0
for Maven 2, Maven 3, and Maven 4 (as of now).This version number hasn’t changed for years because:
The POM structure has remained stable.
Backward compatibility is a core principle in Maven.
C. Historical Context
Maven 1.x had a different POM format (no
<modelVersion>
at all).When Maven 2 was released, they introduced POM v4.0.0 to unify how build configuration was defined.
Future changes (if Maven 5 introduces POM v5.0.0) would require updating this field.
D. What Happens if We Change It ?
If we set it to something unsupported, Maven will throw:
Non-parseable POM
This is one of the very few non-optional POM elements.
E. Best Practice
Keep it as the very first child of
<project>
.Always use:
<modelVersion>4.0.0</modelVersion>
Don’t remove it, even though Maven might sometimes seem to work without it in IDE-generated POMs.
Project Coordinates
From our sample POM:
<groupId>com.example</groupId>
<artifactId>sample-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
These four elements tell Maven:
What our project is called
Who owns it
Which version is being built
What type of artifact is produced
They are collectively known as GAV (GroupId, ArtifactId, Version) + Packaging.
A. <groupId>
<groupId>
Defines the organization or group that produces the project.
Usually follows reverse domain naming:
Example:
com.google.guava
For internal projects:
com.companyname.project
It ensures global uniqueness in repositories.
Why reverse domain ?
Domains are unique to an organization, so reversing them avoids clashes.
Example:
com.example
→ no other company will accidentally use our namespace.
B. <artifactId>
<artifactId>
The name of the built artifact (without version or extension).
Together with
groupId
, it must be unique.Examples:
spring-core
hibernate-entitymanager
sample-app
When packaged, Maven appends:
<artifactId>-<version>.<packaging>
Example:
sample-app-1.0.0-SNAPSHOT.jar
C. <version>
<version>
The current version of the project.
Formats:
Release:
1.0.0
,2.1.5
Snapshot:
1.0.0-SNAPSHOT
Snapshot means:
Work in progress
Can change without notice
Maven won’t cache aggressively — it checks for updates on each build
Release means:
Immutable
Stored permanently in repository without overwriting
Always use SNAPSHOT during development, and set a proper release version before publishing.
D. <packaging>
<packaging>
Defines the type of artifact Maven should create.
Common values:
jar
— Java librarywar
— Web applicationpom
— Parent or aggregator POMear
— Enterprise Archiverar
— Resource Adapter Archive
If omitted, defaults to
jar
.
E. How Maven Uses GAV ?
Maven stores artifacts in the repository structure:
~/.m2/repository/<groupId path>/<artifactId>/<version>/<artifactId>-<version>.<packaging>
Example for our sample:
~/.m2/repository/com/example/sample-app/1.0.0-SNAPSHOT/sample-app-1.0.0-SNAPSHOT.jar
F. Best Practices
Uniqueness: Ensure
groupId
+artifactId
combination is unique across all repositories we publish to.Semantic Versioning: Use
MAJOR.MINOR.PATCH
format for versions.Packaging Consistency: Keep packaging type aligned with project type (don’t package a web app as
jar
unless using Spring Boot’s embedded server).
Project Metadata
From our sample:
<name>Sample Maven Application</name>
<description>An example project demonstrating all POM sections</description>
<url>https://example.com/sample-app</url>
A. <name>
<name>
A readable project name — not necessarily the same as
artifactId
.Appears in:
Generated project documentation (
mvn site
)IDE displays
Maven Central listings
Example:
<name>Apache Commons IO</name>
vs.
<artifactId>commons-io</artifactId>
B. <description>
<description>
A one- or two-line summary of our project’s purpose.
Visible in generated Maven reports, repository browsers, and artifact search tools.
Should be:
Clear — avoid jargon unless our audience knows it
Concise — a few sentences at most
Example:
<description>A utility library for working with IO in Java</description>
C. <url>
<url>
The official homepage or documentation URL for the project.
Can point to:
Project website
GitHub repository
Internal wiki (for private projects)
Example:
<url>https://github.com/apache/commons-io</url>
D. Why Metadata Matters ?
Discoverability — if someone finds our artifact on Maven Central, they should immediately know what it does and where to learn more.
Documentation Integration — Maven site plugins and reporting tools pull these values automatically.
Team Communication — in large organizations, metadata helps new developers quickly grasp project purpose.
E. Best Practices
Always include
<name>
and<description>
even in private/internal projects - future maintainers will thank us.Keep
<url>
up to date. Nothing says “abandoned” like a broken homepage link.Treat this like a business card for our artifact.
The <parent>
Section
<parent>
SectionFrom our sample:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/> <!-- Look up parent from repository -->
</parent>
A. Purpose
The <parent>
element allows our project to inherit:
Dependency versions
Plugin configurations
Properties
Build settings
Instead of redefining them in every project, we can define them once in a parent POM and let child POMs inherit them.
B. Structure
<groupId>
The parent project’s group ID.<artifactId>
The parent project’s artifact ID.<version>
The parent POM’s version we want to use.<relativePath>
(optional)If the parent POM is in our local filesystem (multi-module build), Maven uses this path to locate it.
Default:
../pom.xml
Setting it to empty (
<relativePath/>
) forces Maven to look in the repository instead.
C. How Inheritance Works ?
If the child POM omits certain elements (like
<properties>
,<dependencyManagement>
,<build>
), Maven will merge them from the parent POM.If the child redefines an element, it overrides the parent’s value.
We can even have multi-level inheritance (parent → grandparent POM).
D. Real-World Examples
Spring Boot uses
spring-boot-starter-parent
to:Predefine common dependency versions (BOM style)
Configure compiler & test plugins
Set default encoding, resource filtering, etc.
Large organizations create internal parent POMs to enforce:
Coding standards (plugin rules)
Common logging frameworks
Unified dependency versions
E. Inheritance vs. Aggregation
Inheritance (
<parent>
) — share configuration; child still builds independently.Aggregation (
<modules>
) — build multiple modules in one go; no automatic config sharing unless they also share a parent.
F. Best Practices
If using a parent from a public framework (like Spring Boot), always match versions carefully with the framework we are using.
For internal projects, create a custom parent POM for consistency across all microservices/libraries.
Avoid deep inheritance chains — they make troubleshooting difficult.
The <properties>
Section
<properties>
SectionFrom our sample:
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
</properties>
A. Purpose
Defines custom variables that can be reused throughout the POM.
Makes the configuration more maintainable and less repetitive.
Allows for centralized updates — change one property, and it updates everywhere it’s used.
B. How Properties Work ?
We reference a property with
${propertyName}
.Maven interpolates (replaces) these placeholders before executing the build.
Properties can:
Be custom-defined in
<properties>
Come from Maven itself (built-in)
Be passed via CLI (
mvn clean install -Dproperty=value
)
C. Common Use Cases
Java version settings
<java.version>17</java.version>
Used by compiler and other plugins.
Encoding
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Timestamp formatting
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
Plugin or dependency versions
<spring.boot.version>3.1.1</spring.boot.version>
Then:
<version>${spring.boot.version}</version>
D. Built-In Maven Properties
Maven comes with predefined variables we can use without declaring them:
${basedir}
— root directory of the project${project.version}
— version from<version>
tag${project.build.directory}
— usuallytarget
${settings.localRepository}
— path to local repo (~/.m2/repository
)${java.home}
— path to Java installation
E. CLI-Defined Properties
We can override or add properties at runtime:
mvn clean package -Djava.version=21
This will temporarily set ${java.version}
to 21
for that build.
F. Best Practices
Centralize versions in
<properties>
— avoids “version drift” in dependencies/plugins.Use meaningful names for custom properties — avoid overly generic names like
<version>
.Don’t hardcode Java or encoding values in multiple places — use a single property reference.
Be cautious with overriding properties via CLI in CI/CD — it can cause inconsistent builds if not documented.
The <dependencyManagement>
Section
<dependencyManagement>
SectionFrom our sample:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
</dependencyManagement>
A. Purpose
Acts like a version control center for dependencies.
Lets us declare versions once and reuse them across multiple modules without repeating the version each time.
Especially useful in multi-module projects.
B. How It Works ?
Dependencies inside
<dependencyManagement>
are not automatically included in the build.They only define defaults for:
version
scope
exclusions
To actually use them, we still declare them in
<dependencies>
— but without the version.
Example:
<!-- In parent POM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.1</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- In child POM -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- No version needed -->
</dependency>
</dependencies>
Maven will automatically inject 3.1.1
from the parent.
C. Benefits
Consistency — no accidental version mismatches between modules.
Centralized updates — bump a version in one place, all modules get it.
Cleaner child POMs — less duplication, more readable.
D. Typical Uses
Multi-module projects — Parent POM defines versions, child POMs just declare dependencies without versions.
BOM (Bill of Materials) imports — Import another project’s dependency versions (e.g., Spring Boot’s BOM).
Example of BOM import:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
This imports all managed versions from Spring Boot’s BOM.
E. Best Practices
Keep
<dependencyManagement>
only in parent POMs — avoid in regular modules unless they’re BOMs themselves.Avoid mixing managed and unmanaged versions for the same dependency — it creates confusion.
Prefer BOM imports for large frameworks instead of manually listing every dependency.
The <dependencies>
Section
<dependencies>
SectionFrom our sample:
<dependencies>
<!-- Web framework -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok for boilerplate code reduction -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Testing library -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
A. Purpose
Lists all the external and internal libraries our project depends on.
Maven automatically:
Downloads them from repositories.
Adds them to the classpath.
Handles transitive dependencies (dependencies of our dependencies).
B. Key Elements
Each <dependency>
typically has:
<groupId>
— Organization or project namespace.<artifactId>
— Unique name of the library/module.<version>
— The specific release we want.Optional if managed in
<dependencyManagement>
or imported BOM.
<scope>
— Defines where the dependency is available (more on this below).<optional>
(optional) — Iftrue
, not included transitively by projects that depend on us.<exclusions>
(optional) — Used to block unwanted transitive dependencies.
C. Dependency Scopes
compile (default)
All phases (compile, test, runtime)
Core libraries our app always needs
provided
Compile + test, not runtime
Servlet API (container provides it)
runtime
Test + runtime, not compile
JDBC driver
test
Only in test classpath
JUnit, Mockito
system
Like provided, but requires explicit local path
Rarely used, for local JARs
import
Only in <dependencyManagement>
For BOM imports
D. Transitive Dependencies
If our dependency depends on something else, Maven pulls it in automatically. Example:
We add
spring-boot-starter-web
Maven fetches:
Spring MVC
Jackson
Tomcat
And so on...
We can exclude unwanted transitive dependencies:
<dependency>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
E. Best Practices
Use BOM +
<dependencyManagement>
to avoid repeating versions.Be careful with scope=provided — using it incorrectly can cause runtime
ClassNotFoundException
.Regularly check for unused dependencies (
mvn dependency:analyze
).
The <build>
Section
<build>
SectionFrom our sample:
<build>
<finalName>my-app</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.example.MainApp</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
A. Purpose
Defines how Maven builds our project:
Output naming
Plugins to run
Compiler and packaging settings
Directory configurations
B. Key Elements
1. <finalName>
Sets the name of the generated artifact without extension.
Example:
finalName
=my-app
Packaging =
jar
Output file =
my-app.jar
2. <plugins>
Plugins are Maven’s workhorses — they handle compilation, packaging, testing, deployment, etc.
Inside each
<plugin>
:<groupId>
— Usuallyorg.apache.maven.plugins
for official plugins.<artifactId>
— Plugin name (e.g.,maven-compiler-plugin
).<version>
— Specific plugin version.<configuration>
— Custom settings for that plugin.
C. Commonly Used Plugins
Compiler Plugin
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin>
Controls Java source/target version.
JAR Plugin
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>com.example.MainApp</mainClass> </manifest> </archive> </configuration> </plugin>
Configures how JAR files are packaged and adds metadata.
Surefire Plugin (for tests)
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.1.2</version> </plugin>
D. Advanced Build Customization
The <build>
section can also include:
<resources>
— Non-Java files to include in build.<testResources>
— Test-specific resources.<directory>
— Change the default output dir (default:target
).<filters>
— Filter resources with property substitution.
Example:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
E. Best Practices
Always pin plugin versions — Maven defaults can change unexpectedly.
Group related plugin configs together for readability.
Avoid overloading
pom.xml
with complex plugin logic — use external config files if necessary.Keep
<finalName>
meaningful for CI/CD pipelines.
The <profiles>
Section
<profiles>
SectionFrom our sample:
<profiles>
<profile>
<id>dev</id>
<properties>
<env>development</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.7.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>prod</id>
<properties>
<env>production</env>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
</profiles>
A. Purpose
Profiles let us change Maven’s behavior based on conditions.
We can define:
Different dependencies
Different build plugins
Different properties
Different resource directories
Useful for dev vs test vs prod builds, or platform-specific settings.
B. Key Elements
1. <id>
Unique profile name.
Activated with:
mvn clean install -Pprod
2. <activation>
Defines conditions for automatic activation:
<activeByDefault>
— Always active unless another profile is explicitly chosen.<jdk>
— Activates based on JDK version.<os>
— Activates based on OS name, family, arch, version.<property>
— Activates if a system property is present.<file>
— Activates if a file exists (or not).
Example:
<activation>
<property>
<name>env</name>
<value>production</value>
</property>
</activation>
3. Profile-specific sections
<dependencies>
— Add dependencies only for that profile.<build>
— Override or add build settings for that profile.<properties>
— Define environment-specific variables.
C. How Profiles Work
Without profiles — All settings apply to all builds.
With profiles — Only active profiles’ settings merge into the final build configuration.
Maven merges:
Global settings (in
settings.xml
)Active profile settings (from
pom.xml
orsettings.xml
)Command-line arguments
D. Typical Use Cases
Switching database drivers between dev (HSQLDB) and prod (MySQL).
Enabling/disabling debug logging.
Using different Java versions for builds.
Packaging different resource sets.
E. Best Practices
Use profiles for environmental differences, not for optional features in the same environment.
Avoid too many profiles - it can make builds unpredictable.
Always document profile purposes for other developers.
The <repositories>
Section
<repositories>
SectionExample:
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
A. Purpose
Defines where Maven should look for dependencies besides the default Maven Central repository.
Useful when:
Using internal corporate artifact repositories (like Nexus or Artifactory)
Using third-party repositories (e.g., JitPack, Spring Milestone)
Accessing snapshot versions not in Maven Central
B. Key Elements
1. <id>
Unique identifier for the repository.
Used in logs, dependency resolution, and possible plugin configs.
2. <url>
Repository base URL.
Must point to a valid Maven repository structure.
3. <releases>
and <snapshots>
Control whether the repo is used for release artifacts, snapshot artifacts, or both.
<enabled>
can betrue
orfalse
.
Example:
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
4. Authentication
For private repos, credentials are stored in
settings.xml
, not thepom.xml
.Example in
settings.xml
:
<servers>
<server>
<id>internal-repo</id>
<username>myuser</username>
<password>mypassword</password>
</server>
</servers>
The
<id>
here must match the<id>
in<repository>
.
C. How Maven Resolves Dependencies
Local Repository — Checks
~/.m2/repository
first.Remote Repositories — Checks in order defined in
<repositories>
.Mirror Settings — Can redirect all requests via
settings.xml
.
D. Best Practices
Do not overuse external repositories — it can slow builds and create dependency conflicts.
Always pin dependencies to specific versions when using non-central repos.
Use mirrors in
settings.xml
for centralizing access to multiple repositories.For corporate projects, prefer a single internal proxy to external repositories.
The <pluginRepositories>
Section
<pluginRepositories>
SectionExample:
<pluginRepositories>
<pluginRepository>
<id>apache-plugins</id>
<url>https://repo.maven.apache.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>custom-plugin-repo</id>
<url>https://plugins.example.com/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
A. Purpose
Specifies where Maven should look for build plugins other than the default Maven Central repository.
Maven uses separate repository lists for plugins and dependencies.
Needed if:
We are using a plugin not hosted on Maven Central
Our organization hosts custom build plugins internally
B. Key Elements
1. <pluginRepository>
Similar structure to
<repository>
:<id>
— Unique name<url>
— Repository location<releases>
/<snapshots>
— Enable/disable artifact types
2. Authentication
Just like normal repositories, credentials go into
settings.xml
:
<servers>
<server>
<id>custom-plugin-repo</id>
<username>builduser</username>
<password>secret</password>
</server>
</servers>
3. Mirrors
We can redirect all plugin repo lookups through a mirror in
settings.xml
.
C. Difference from <repositories>
<repositories>
<repositories>
<pluginRepositories>
Controls where Maven searches for JARs, libraries, artifacts
Controls where Maven searches for build plugins
Used during dependency resolution
Used when fetching plugins for build lifecycle phases
D. Best Practices
Avoid mixing dependency repositories with plugin repositories unless necessary.
Keep custom plugins documented without a central repo, future builds may fail.
For corporate environments, use an internal mirror/proxy to cache plugin artifacts.
The <distributionManagement>
Section
<distributionManagement>
SectionExample:
<distributionManagement>
<repository>
<id>releases-repo</id>
<name>Company Releases</name>
<url>https://repo.company.com/maven2/releases</url>
</repository>
<snapshotRepository>
<id>snapshots-repo</id>
<name>Company Snapshots</name>
<url>https://repo.company.com/maven2/snapshots</url>
</snapshotRepository>
<site>
<id>project-site</id>
<name>Project Documentation</name>
<url>scp://docs.company.com/www/project</url>
</site>
<downloadUrl>https://downloads.company.com/project</downloadUrl>
</distributionManagement>
A. Purpose
Tells Maven where to deploy built artifacts (JAR, WAR, POM, etc.).
Defines separate locations for:
Release artifacts (stable versions)
Snapshot artifacts (in-progress versions)
Generated project site (HTML documentation)
Works together with
mvn deploy
phase.
B. Key Elements
1. <repository>
Destination for release versions.
Only accepts non-SNAPSHOT version numbers.
Example:
<repository>
<id>releases-repo</id>
<url>https://repo.company.com/maven2/releases</url>
</repository>
2. <snapshotRepository>
Destination for SNAPSHOT versions.
SNAPSHOT builds allow overwriting — they are not immutable.
3. <site>
Where Maven publishes the site documentation (
mvn site:deploy
).
4. <downloadUrl>
Optional — URL for users to manually download artifacts outside Maven.
C. Authentication
Credentials must not be stored in
pom.xml
— put them insettings.xml
:
<servers>
<server>
<id>releases-repo</id>
<username>deployuser</username>
<password>deploypass</password>
</server>
</servers>
The
<id>
here matches the<id>
in<distributionManagement>
.
D. How It Works ?
We run:
mvn clean deploy
Maven:
Builds our project
Creates the POM and packaged artifact
Pushes it to the release or snapshot repo based on the version
E. Best Practices
Always separate release and snapshot repositories.
Keep deployment credentials outside of source control.
Automate deployment in CI/CD pipelines to prevent manual errors.
Avoid deploying directly to public repos without review.
The <reporting>
Section
<reporting>
SectionExample:
<reporting>
<outputDirectory>${project.build.directory}/site</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.4.5</version>
<reportSets>
<reportSet>
<reports>
<report>dependencies</report>
<report>plugins</report>
<report>licenses</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<reportSets>
<reportSet>
<reports>
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
A. Purpose
Controls Maven’s site reporting system.
Defines which reports are generated when we run:
mvn site
These reports are often published to a website or artifact repository for:
Project documentation
Code quality analysis
Dependency tracking
B. Key Elements
1. <outputDirectory>
Where the generated site is stored locally (default:
${project.build.directory}/site
).
2. <plugins>
List of reporting plugins.
Important: This
<plugins>
inside<reporting>
is different from the one inside<build>
— it’s used only for generating reports, not building the project.
3. <reportSets>
Defines specific sets of reports a plugin should generate.
Allows filtering which reports run for different situations.
C. Common Reporting Plugins
maven-project-info-reports-plugin
Generates project info: dependencies, plugins, licenses, SCM, etc.
maven-javadoc-plugin
Generates JavaDocs for our codebase.
jacoco-maven-plugin
Generates code coverage reports.
maven-surefire-report-plugin
Creates test result reports.
D. Best Practices
Don’t overload
<reporting>
with unnecessary plugins — large report sets slow down site generation.Use it in combination with CI/CD pipelines to publish reports automatically.
Keep plugin versions pinned to avoid sudden output changes.
Project Metadata Sections
These elements don’t directly affect compilation or packaging, but they are critical for documentation, open-source compliance, and automated tooling.
A. <licenses>
<licenses>
Example:
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
<comments>Standard open source license.</comments>
</license>
</licenses>
Purpose:
Declares the license(s) our project is distributed under.
Used by tools like Maven Central’s validation process and license scanners.
Key Fields:
<name>
— Full license name.<url>
— Link to license text.<distribution>
— Usuallyrepo
(distributed with repo) ormanual
.<comments>
— Optional notes about the license.
B. <developers>
<developers>
Example:
<developers>
<developer>
<id>pranay</id>
<name>Pranay Pourkar</name>
<email>[email protected]</email>
<url>https://pranaypourkar.dev</url>
<organization>ConceptJotter</organization>
<organizationUrl>https://conceptjotter.dev</organizationUrl>
<roles>
<role>Developer</role>
<role>Architect</role>
</roles>
<timezone>+5:30</timezone>
</developer>
</developers>
Purpose:
Lists the main contributors to the project.
Important for open-source projects to give credit and contact points.
Key Fields:
<id>
— Short unique identifier.<roles>
— Multiple roles possible (developer, maintainer, tester, etc.).<timezone>
— Useful for distributed teams.
C. <contributors>
<contributors>
Example:
<contributors>
<contributor>
<name>Jane Doe</name>
<email>[email protected]</email>
<roles>
<role>Tester</role>
</roles>
</contributor>
</contributors>
Purpose:
Recognizes people who contributed but aren’t core developers.
D. <scm>
(Source Control Management)
<scm>
(Source Control Management)Example:
<scm>
<connection>scm:git:https://github.com/username/project.git</connection>
<developerConnection>scm:git:ssh://[email protected]/username/project.git</developerConnection>
<url>https://github.com/username/project</url>
<tag>v1.0.0</tag>
</scm>
Purpose:
Tells Maven and plugins where the source code repository lives.
Useful for:
Automated release tagging
Generating SCM links in reports
Continuous Integration pipelines
Key Fields:
<connection>
— Read-only access URL.<developerConnection>
— Write-access URL for maintainers.<url>
— Web UI link to the repo.<tag>
— Tag name for the current release.
E. <issueManagement>
<issueManagement>
Example:
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/username/project/issues</url>
</issueManagement>
Purpose:
Indicates where bugs and feature requests are tracked.
F. <ciManagement>
<ciManagement>
Example:
<ciManagement>
<system>GitHub Actions</system>
<url>https://github.com/username/project/actions</url>
</ciManagement>
Purpose:
Documents the Continuous Integration system for the project.
Best Practices
Always fill out
<licenses>
if publishing publicly — Maven Central will reject uploads without it.Include
<scm>
and<developers>
for professional credibility.Keep metadata updated with each major release.
For open-source work, align
<license>
and repositoryLICENSE
file.
Complete Example of pom.xml
with All Sections
pom.xml
with All Sections<?xml version="1.0" encoding="UTF-8"?>
<!-- Maven Project Object Model (POM) File -->
<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">
<!-- Model Version -->
<modelVersion>4.0.0</modelVersion>
<!-- Project Coordinates -->
<groupId>com.example</groupId>
<artifactId>demo-project</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- Project Information -->
<name>Demo Project</name>
<description>A sample Maven project demonstrating all POM sections.</description>
<url>https://example.com/demo-project</url>
<!-- Project Properties -->
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- Dependency Management -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Project Dependencies -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Repositories for Dependencies -->
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<!-- Build Configuration -->
<build>
<finalName>demo-project</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
</plugin>
</plugins>
</build>
<!-- Profiles for Different Builds -->
<profiles>
<profile>
<id>dev</id>
<properties>
<env>development</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>production</env>
</properties>
</profile>
</profiles>
<!-- Distribution Management for Deployment -->
<distributionManagement>
<repository>
<id>releases-repo</id>
<url>https://repo.company.com/releases</url>
</repository>
<snapshotRepository>
<id>snapshots-repo</id>
<url>https://repo.company.com/snapshots</url>
</snapshotRepository>
<site>
<id>project-site</id>
<url>scp://docs.company.com/www/project</url>
</site>
<downloadUrl>https://downloads.company.com/demo-project</downloadUrl>
</distributionManagement>
<!-- Reporting Configuration -->
<reporting>
<outputDirectory>${project.build.directory}/site</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.4.5</version>
<reportSets>
<reportSet>
<reports>
<report>dependencies</report>
<report>plugins</report>
<report>licenses</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<reportSets>
<reportSet>
<reports>
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
<!-- Project Metadata -->
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>pranay</id>
<name>Pranay Pourkar</name>
<email>[email protected]</email>
<roles>
<role>Developer</role>
</roles>
</developer>
</developers>
<contributors>
<contributor>
<name>Jane Doe</name>
<roles>
<role>Tester</role>
</roles>
</contributor>
</contributors>
<scm>
<connection>scm:git:https://github.com/username/demo-project.git</connection>
<developerConnection>scm:git:ssh://[email protected]/username/demo-project.git</developerConnection>
<url>https://github.com/username/demo-project</url>
<tag>v1.0.0</tag>
</scm>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/username/demo-project/issues</url>
</issueManagement>
<ciManagement>
<system>GitHub Actions</system>
<url>https://github.com/username/demo-project/actions</url>
</ciManagement>
</project>
Last updated