Maven Fundamentals & Why Maven Exists
Goal: Understand why Maven was created before learning commands.
Throughout this page, we will follow one small project: a Student Report App. It reads student marks and saves a report as a JSON file. Simple idea, but it is enough to show every problem Maven solves.
What is Maven?
Maven is a tool that manages and builds Java projects for you.
It does three main jobs:
- Downloads the libraries your project needs.
- Builds your project: compiles the code, runs the tests, and packages everything into a single file.
- Gives every project the same structure, so any Java developer can open your project and know where things are.
You describe your project in one file called pom.xml. Maven reads it and does the rest.
For example, our Student Report App needs a library called Jackson to create JSON. With Maven, we just write this in pom.xml:
1<dependency>2 <groupId>com.fasterxml.jackson.core</groupId>3 <artifactId>jackson-databind</artifactId>4 <version>2.17.0</version>5</dependency>Maven downloads Jackson, plus everything Jackson itself needs.
That might not sound impressive yet. To see why it matters, let's look at what building a Java project involves without Maven.
Why do we need a build/project management tool?
Writing code is only part of the work. To turn the Student Report App into something you can run or share, you have to:
- Find and download the libraries it uses (like Jackson).
- Compile every
.javafile. - Run the tests.
- Package the compiled code into a file you can share.
- Do all of this again every time the code changes.
Doing these steps by hand is slow, and it is easy to forget one. It gets worse in a team:
- Each developer may download a different library version.
- Each developer may build the project a different way.
- The project works on one laptop and fails on another.
A build tool solves this by writing the steps down once. After that, everyone builds the project the same way with one command, on any machine.
Maven is one such tool, and one of the most widely used in Java.
Maven vs manual JAR management
Before Maven, Java developers managed libraries by hand. Here's what adding Jackson to our app looks like both ways:
| Task | Without Maven | With Maven |
|---|---|---|
| Get a library | Search the web, download the .jar file | Add a few lines to pom.xml |
| Get the library's own needs | Find and download each one yourself | Downloaded automatically |
| Make Java find them | Build a long classpath by hand | Done automatically |
| Upgrade a version | Delete old files, download new ones, fix the classpath | Change one version number |
| Share with your team | Commit .jar files or send them around | Share only pom.xml |
Don't worry if words like JAR, dependency, and classpath are new. The rest of this page explains each one, and by the end you will see exactly why the "Without Maven" column becomes painful.
Let's start with the file that appears in almost every row: the JAR.
What is a .jar file?
A JAR (Java ARchive) is a single file that bundles many compiled Java files together.
It is basically a zip file. If you rename app.jar to app.zip and open it, you can see what's inside:
1student-report.jar2├── com/example/Main.class3├── com/example/Student.class4├── com/example/ReportWriter.class5└── META-INF/MANIFEST.MF- The
.classfiles are your compiled code. MANIFEST.MFis a small text file with information about the JAR.
Instead of sharing hundreds of separate files, you share one JAR.
But where do those .class files come from? That's the next step.
.java → .class → .jar
Your code goes through three stages:
1Main.java → Main.class → student-report.jar2(you write) (compiled) (packaged)1. .java: the code you write. It is plain text that humans can read.
2. .class: compiled code. The javac compiler turns each .java file into a .class file containing bytecode. The Java Virtual Machine (JVM) runs bytecode, not .java files.
1javac -d out src/com/example/*.java3. .jar: packaged code. All the .class files are bundled into one JAR.
1jar cf student-report.jar -C out .So a JAR is simply the final, shareable form of Java code.
Our Student Report App ends up as a JAR. Jackson is also a JAR. But they are very different kinds of JAR, which brings us to the next idea.
Library vs Application
An application is a program you run. It has a starting point: the main method.
Our Student Report App is an application. You run it, and it produces a report.
A library is code written for other programs to use. You don't run it on its own. It has no main method meant for you to start.
Jackson is a library. It knows how to turn Java objects into JSON, but it does nothing until an application calls it.
Think of a car:
- The application is the car you drive.
- The libraries are parts like the engine and tyres. They are essential, but you don't drive an engine by itself.
Our app uses Jackson so we don't have to write JSON code ourselves. Most real applications use many libraries this way.
Executable JAR vs normal/library JAR
Since there are two kinds of code, there are two kinds of JAR.
An executable JAR contains an application and can be started directly:
1java -jar student-report.jarThis works because its MANIFEST.MF says which class to start from:
1Main-Class: com.example.MainA normal (library) JAR has no Main-Class entry. You can't run it. Its only job is to provide code to other programs, like jackson-databind.jar.
| Executable JAR | Library JAR | |
|---|---|---|
| Example | student-report.jar | jackson-databind.jar |
Has Main-Class in manifest | Yes | No |
Can run with java -jar | Yes | No |
| Purpose | Start an application | Be used by other code |
Our executable JAR still needs Jackson's code to work. Something our code needs like this has a name: a dependency.
What is a dependency?
A dependency is a library your project needs in order to compile or run.
Our Student Report App depends on Jackson. Without Jackson, it can't create JSON.
Here's the catch: libraries have dependencies too. jackson-databind itself needs two more libraries:
1student-report-app2└── jackson-databind3 ├── jackson-core4 └── jackson-annotations- Direct dependency: a library you chose to use (
jackson-databind). - Transitive dependency: a library that your library needs (
jackson-core,jackson-annotations).
We only asked for one library, but our app actually needs three JARs.
So our app needs all three JARs. But having them in a folder is not enough. Java has to be told where to find them, and it does that through the classpath.
What is the classpath?
The classpath is the list of places where Java looks for .class files when your program runs.
When our app calls Jackson, the JVM searches the classpath for Jackson's classes. If it can't find them, the program crashes with an error like:
1java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapperThink of the classpath as a shopping list for the JVM. Anything not on the list, the JVM won't find, even if the file is sitting right there on your computer.
Without Maven, you write this list yourself:
1java -cp "out:lib/jackson-databind.jar:lib/jackson-core.jar:lib/jackson-annotations.jar" com.example.Main(On Windows, use ; instead of : between paths.)
That's three JARs for one library. Now imagine a real project.
Why manual dependency management becomes difficult
Let's put everything together. Without Maven, adding one library to our app means:
- Finding
jackson-databind.jaronline and downloading it. - Discovering it needs
jackson-coreandjackson-annotations, and downloading those too. - Making sure all three versions work together.
- Adding all three to the classpath.
- Sharing all three with your team.
That's for one library. Real projects often use dozens of libraries, which pull in many more transitive ones. Now the problems multiply:
Hidden dependencies. You often find out a JAR is missing only when the program crashes with a NoClassDefFoundError.
Version conflicts. Suppose two libraries both need jackson-core, but different versions. Only one version can be on the classpath, so you must figure out which one works for both.
Long, fragile classpaths. One typo in a long classpath, and the app fails to start.
Painful upgrades. Upgrading one library can mean replacing several JARs and fixing the classpath again.
Team mismatch. Everyone needs the exact same JARs. If one developer has a different version, the app behaves differently on their machine.
This is the problem Maven was created to solve.
Remember the few lines we wrote in pom.xml at the start? With them, Maven:
- downloads
jackson-databind, - finds and downloads
jackson-coreandjackson-annotationsautomatically, - picks versions that work together,
- builds the classpath for you,
- and gives your whole team the exact same setup from one shared file.
Now that you know why Maven exists, the next step is learning how to use it.