Skip to content
Educora
Advanced20 min13 / 18

Maven and Gradle: building a project

Learn the standard structure of a real Java project, how dependencies are declared in `pom.xml` and `build.gradle.kts`, and how the Maven lifecycle works.

Check yourself
In this lesson you will learn
  • Recognise the standard folder layout of a Maven/Gradle project
  • Add a dependency by its coordinates (groupId:artifactId:version) with the right scope
  • Run Maven lifecycle phases and Gradle tasks

Until now every program was a single Main.java file started with java Main.java. A real project, however, has hundreds of classes, dozens of external libraries (JSON, a database driver, a test framework) and several people working on it. Downloading every JAR file by hand and passing a long -cp list to javac quickly becomes a nightmare. Build tools do this job: Maven and Gradle download libraries for you, compile the code, run the tests and package a ready JAR file.

The standard project layout

Maven follows the principle of convention over configuration: files always live in the same places, so you do not have to tell the tool where to look for them. Gradle uses the same layout. The main code is in src/main/java, the tests in src/test/java, and configuration files and images in src/main/resources. A package name becomes folders: the com.educora package is the com/educora folder. The build result goes to target/ in Maven and to build/ in Gradle.

Text
grade-book/
├── pom.xml                  ← Maven (Gradle: build.gradle.kts)
├── src/
│   ├── main/
│   │   ├── java/com/educora/App.java
│   │   └── resources/config.properties
│   └── test/
│       └── java/com/educora/AppTest.java
└── target/                  ← build output (Gradle: build/)
Typical layout of a Maven or Gradle project
Definition
Dependency

An external library your project needs in order to work. It is identified by three coordinates: groupId (the organisation, e.g. com.google.code.gson), artifactId (the library name, gson) and version (2.11.0). Short form: com.google.code.gson:gson:2.11.0. The tool downloads the library from the Maven Central repository and keeps it in a cache on your computer.

Maven and pom.xml

The heart of a Maven project is the **pom.xml file (Project Object Model). It holds the project's own coordinates, the Java version and the list of dependencies. The file below has two dependencies: Gson for working with JSON and JUnit for tests. The libraries that they need themselves — the transitive dependencies** — are downloaded by Maven automatically.

XML
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.educora</groupId>
  <artifactId>grade-book</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.release>21</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.11.0</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.11.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
pom.xml: maven.compiler.release compiles the code for Java 21

Note the <scope>test</scope> line: JUnit is needed only in tests and must not end up in the finished program. The scope decides where a dependency can be used:

ScopeWhere it is visibleExample
compileeverywhere: main code, tests and the running program (the default)Gson
testonly in src/test/javaJUnit, Mockito
providedat compile time; the server supplies it at run timeServlet API
runtimeonly at run time, not for compilingJDBC driver

The Maven lifecycle

In Maven, commands are lifecycle phases that run in a strict order. When you call a phase, Maven also runs every phase before it: mvn package first validates the project, compiles it and runs the tests, and only then creates the JAR. If a test fails, the build stops — broken code is not packaged. clean is a separate phase: it deletes the old target/ folder.

PhaseWhat it does
validatechecks that the project is correct
compilecompiles src/main/java into target/classes
testruns the tests in src/test/java
packagecreates the JAR (or WAR) file
verifyruns extra checks such as integration tests
installputs the JAR into the local repository (~/.m2)
deployuploads the JAR to the team's remote repository
Terminal
mvn clean package
Expected output
[INFO] Scanning for projects...
[INFO] Building grade-book 1.0.0
[INFO] --- compiler:3.13.0:compile (default-compile) @ grade-book ---
[INFO] --- surefire:3.2.5:test (default-test) @ grade-book ---
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO] --- jar:3.4.1:jar (default-jar) @ grade-book ---
[INFO] Building jar: C:\projects\grade-book\target\grade-book-1.0.0.jar
[INFO] BUILD SUCCESS
Sample output (shortened): plugin versions and paths may differ on your machine

Gradle and build.gradle.kts

Gradle does the same job with a script that looks like program code instead of XML. In modern projects the script is written in Kotlin — build.gradle.kts (in older projects you will also see build.gradle, written in Groovy). Gradle has tasks instead of phases: compileJava, test, jar, build. Gradle does not rebuild parts that have not changed and caches results, so in large projects it is usually faster than Maven. Android apps are built with Gradle too.

Kotlin
plugins {
    application
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.google.code.gson:gson:2.11.0")
    testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = "com.educora.App"
}

tasks.test { useJUnitPlatform() }
build.gradle.kts: implementation matches Maven's compile scope and testImplementation the test scope

The application plugin adds the ./gradlew run task: it starts the class named in mainClass together with all its libraries. The toolchain block tells Gradle to build the project with exactly Java 21. Compare building by hand with Gradle:

Without a tool: everything by hand
# download every JAR by hand into lib/, then:
javac -d out -cp lib/gson-2.11.0.jar src/com/educora/*.java
java -cp "out:lib/gson-2.11.0.jar" com.educora.App
# a new library? download it, fix both commands again...
Gradle: two short commands
# dependencies are listed once in build.gradle.kts
./gradlew build   # download, compile, test, package
./gradlew run     # start the app with all its libraries
TaskMavenGradle
Compilemvn compile./gradlew compileJava
Run the testsmvn test./gradlew test
Build the JARmvn package./gradlew build
Delete the outputmvn clean./gradlew clean
Dependency treemvn dependency:tree./gradlew dependencies

Create a project step by step

  1. 1
    Check the JDK

    Type java -version in a terminal: the version should be 21 or newer.

  2. 2
    Create the project

    In IntelliJ IDEA choose File › New › Project: language Java, Build system Maven or Gradle, JDK 21. Enter com.educora as GroupId and grade-book as ArtifactId. In a terminal, the gradle init command does the same job.

  3. 3
    Add a dependency

    Find the library's coordinates on the Maven Central website and add them to pom.xml or build.gradle.kts. Then press Reload in the IDE's Maven/Gradle panel so that the library is downloaded.

  4. 4
    Build and run

    mvn package or ./gradlew build runs the tests and creates the JAR; in Gradle, ./gradlew run starts the program right away. The finished JAR is in target/ or build/libs/.

Key points

  • Maven and Gradle download libraries, compile code, run tests and build a JAR.
  • Standard layout: src/main/java, src/main/resources, src/test/java; the output is in target/ or build/.
  • A dependency is written as groupId:artifactId:version; its scope decides where it is visible.
  • Calling a Maven phase runs all earlier phases too: package = validate → compile → test → package.
  • In Gradle you write implementation / testImplementation; the wrapper (./gradlew, ./mvnw) gives everyone the same version.

Check yourself

10 questions. Every correct answer earns XP.

1 / 10
In a Maven project, which folder holds the test classes?