On this page

Maven Project Structure & pom.xml

Goal: Be able to look at a Maven project and understand what everything means.

In the previous page, we saw why Maven exists. Now let's open our Student Report App as a real Maven project and understand every folder and file inside it.

Standard Maven folder structure

Every Maven project follows the same folder layout:

text
1student-report-app/
2├── pom.xml
3├── src/
4│ ├── main/
5│ │ ├── java/
6│ │ │ └── com/example/report/
7│ │ │ ├── Main.java
8│ │ │ ├── Student.java
9│ │ │ └── ReportWriter.java
10│ │ └── resources/
11│ │ └── app.properties
12│ └── test/
13│ ├── java/
14│ │ └── com/example/report/
15│ │ └── ReportWriterTest.java
16│ └── resources/
17│ └── sample-students.json
18└── target/

The idea is simple: main is for your app, test is for testing your app. Inside each, java holds code, and resources holds everything else.

Maven expects this layout by default. You don't have to tell Maven where your code is, because it already knows where to look. This idea is called convention over configuration: follow the standard, and you skip the setup.

It also means any Java developer can open any Maven project and immediately know where things are.

Let's go through each folder.

src/main/java

This is where your application code lives: the code that ends up in the final JAR.

For our app, that's Main.java, Student.java, and ReportWriter.java.

The folders inside match the package name. If a class starts with:

java
1package com.example.report;

then the file must be inside src/main/java/com/example/report/.

Maven compiles everything here into .class files, the same .java → .class step from the previous page, but done for you.

src/main/resources

This folder holds non-code files your app needs while running, such as:

  • configuration files (app.properties)
  • templates
  • text or data files

Our app keeps its settings in app.properties:

text
1report.title=Student Marks Report
2report.output=report.json

Maven copies these files as they are (no compiling) and puts them next to your .class files. So they end up inside the final JAR, and on the classpath.

That's why your code can load them from the classpath instead of using a file path:

java
1InputStream in = Main.class.getResourceAsStream("/app.properties");

This works the same on every machine, because the file travels inside the JAR.

src/test/java

This is where your test code lives.

For our app, ReportWriterTest.java checks that ReportWriter creates correct JSON. Test classes usually use the same package as the code they test, so the folder structure mirrors src/main/java:

text
1src/main/java/com/example/report/ReportWriter.java
2src/test/java/com/example/report/ReportWriterTest.java

Two important rules:

  • Test code is never included in the final JAR. Users of your app don't need your tests.
  • By default, Maven runs test classes whose names end with Test (like ReportWriterTest). Following this naming makes sure your tests actually run.

src/test/resources

This holds files used only by tests, like sample data.

Our test needs some fake student data to work with, so we keep it in sample-students.json:

json
1[
2 { "name": "Asha", "marks": 92 },
3 { "name": "Ravi", "marks": 78 }
4]

Like test code, these files are available only while tests run and are not added to the final JAR.

So far, all four folders hold things you write. The next folder is different: it holds things Maven creates.

target/

target/ is Maven's output folder. Everything Maven builds goes here.

After building our app, it looks like this:

text
1target/
2├── classes/
3│ ├── com/example/report/Main.class
4│ ├── com/example/report/Student.class
5│ ├── com/example/report/ReportWriter.class
6│ └── app.properties
7├── test-classes/
8│ ├── com/example/report/ReportWriterTest.class
9│ └── sample-students.json
10└── student-report-app-1.0.0-SNAPSHOT.jar

Notice where everything went:

You wrote it inMaven put it inIn final JAR?
src/main/javatarget/classes (compiled)Yes
src/main/resourcestarget/classes (copied)Yes
src/test/javatarget/test-classes (compiled)No
src/test/resourcestarget/test-classes (copied)No

Key things to remember:

  • Never edit files in target/. Maven overwrites them on every build.
  • It is safe to delete. Maven recreates it on the next build.
  • Don't commit it to Git. Add target/ to your .gitignore. Your team builds their own.

Notice the JAR's name: student-report-app-1.0.0-SNAPSHOT.jar. Where did Maven get that name? From pom.xml, the file we haven't opened yet.

What is pom.xml?

pom.xml is the heart of a Maven project. POM stands for Project Object Model.

It is one XML file at the root of the project that tells Maven:

  • who the project is (its name and version),
  • what it needs (its dependencies, like Jackson),
  • how to build it (Java version and other build settings).

In the previous page, we saw that Maven builds the classpath and downloads libraries for us. pom.xml is where we tell Maven what to do. The folders tell Maven where things are; pom.xml tells it everything else.

It all starts with the project's identity, which Maven describes using coordinates.

Maven coordinates: groupId, artifactId, version, packaging

Every Maven project is described by a few values called coordinates. Think of them like a postal address: together, they point to exactly one project.

groupId: who made it. Usually your organization's domain name, written in reverse.

xml
1<groupId>com.example</groupId>

artifactId: the project's name. Short, lowercase, words separated by hyphens.

xml
1<artifactId>student-report-app</artifactId>

version: which release.

xml
1<version>1.0.0-SNAPSHOT</version>

A version ending in -SNAPSHOT means "still in development, may change." When you release it, you remove -SNAPSHOT and it becomes a fixed version like 1.0.0, which should never change again.

packaging: what kind of file to build.

xml
1<packaging>jar</packaging>
PackagingBuildsUsed for
jara .jar fileApplications and libraries (the default)
wara .war fileWeb apps deployed to a server like Tomcat
pomno code fileParent projects that group other projects

If you leave out packaging, Maven uses jar.

Now you can see where the JAR's name came from:

text
1artifactId + "-" + version + ".jar"
2student-report-app-1.0.0-SNAPSHOT.jar

These look familiar for a reason: they are the same coordinates we used to add Jackson in the previous page. Jackson is just another Maven project with its own groupId, artifactId, and version.

Let's put it all together into a complete pom.xml.

Basic pom.xml

Here is the full pom.xml for our Student Report App:

xml
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
6 <modelVersion>4.0.0</modelVersion>
7
8 <!-- Who this project is -->
9 <groupId>com.example</groupId>
10 <artifactId>student-report-app</artifactId>
11 <version>1.0.0-SNAPSHOT</version>
12 <packaging>jar</packaging>
13
14 <!-- Build settings -->
15 <properties>
16 <maven.compiler.release>17</maven.compiler.release>
17 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18 </properties>
19
20 <!-- What this project needs -->
21 <dependencies>
22 <dependency>
23 <groupId>com.fasterxml.jackson.core</groupId>
24 <artifactId>jackson-databind</artifactId>
25 <version>2.17.0</version>
26 </dependency>
27
28 <dependency>
29 <groupId>org.junit.jupiter</groupId>
30 <artifactId>junit-jupiter</artifactId>
31 <version>5.10.2</version>
32 <scope>test</scope>
33 </dependency>
34 </dependencies>
35
36</project>

What each part means:

PartMeaning
<project ...>The root tag. The long attributes just tell editors which XML format this is. You can copy them as-is.
modelVersionThe version of the POM format. It is always 4.0.0.
groupId, artifactId, version, packagingThe project's coordinates: who it is.
maven.compiler.releaseWhich Java version to compile for (here, Java 17).
project.build.sourceEncodingRead source files as UTF-8, so special characters work on every machine.
dependenciesThe libraries this project needs.
jackson-databindOur JSON library. Maven also downloads jackson-core and jackson-annotations for it automatically.
junit-jupiter with scope testThe testing library. test scope means it is used only for code in src/test and is not part of the final app.

Notice how small this file is. Compare it to the manual work from the previous page: downloading three JARs, checking versions, writing a long classpath. All of that is replaced by these few lines.

Notice also that every dependency is written using coordinates. That's no accident; coordinates are how Maven finds and tells projects apart.

How Maven identifies a project

Maven identifies every project using three coordinates together:

text
1groupId : artifactId : version

This is often called GAV. Our project's GAV is:

text
1com.example:student-report-app:1.0.0-SNAPSHOT

And Jackson's GAV is:

text
1com.fasterxml.jackson.core:jackson-databind:2.17.0

No two projects in the world should share the same GAV. That's why groupId uses a domain name: you own your domain, so nobody else should use your groupId.

Each part narrows it down:

text
1com.example → all projects from example.com
2com.example:student-report-app → one specific project
3...:1.0.0-SNAPSHOT → one specific version of it

Maven also uses the GAV to decide where to store files on your computer. Every downloaded library is saved in a local folder called ~/.m2/repository, and the path is built from the GAV:

text
1~/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.17.0/jackson-databind-2.17.0.jar
2 └──────── groupId ───────┘ └── artifactId ─┘ └version┘

The dots in groupId become folders. So once Maven downloads Jackson, every project on your computer can reuse the same copy, with no more copying JARs into lib folders.

Here's the big picture: in Maven's world, your project and Jackson are the same kind of thing. Both are identified by a GAV. Both can be built into a JAR. And just as your app depends on Jackson, another project could one day depend on com.example:student-report-app.

Now you can open any Maven project, read its folders and its pom.xml, and understand what everything means. Next, we'll learn the commands that turn this project into a working JAR.

Next TopicMaven Dependencies