Java Academy

Master Java Development

Menu

Core Java

Servlets

JDBC

Microservices

Build Tools

APIs

Build Tools · Lesson 2

Maven Basics and pom.xml

Maven Project Structure and Conventions

Maven enforces a standard directory structure that reduces configuration. Projects follow conventions like src/main/java for source code, src/test/java for tests. This consistency lets Maven figure out what to do without explicit configuration. Understanding Maven's structure is essential for working effectively with Maven-based projects.

Standard Maven Directory Layout

my-app/
├── pom.xml                          # Maven configuration
├── src/
│   ├── main/
│   │   ├── java/                   # Source code
│   │   │   └── com/example/App.java
│   │   └── resources/              # Configuration files, properties
│   │       └── application.properties
│   └── test/
│       ├── java/                   # Test code
│       │   └── com/example/AppTest.java
│       └── resources/              # Test resources
│           └── test.properties
├── target/                          # Build output (generated)
│   ├── classes/                    # Compiled .class files
│   ├── test-classes/               # Compiled test files
│   └── my-app-1.0.0.jar           # Packaged JAR
├── README.md
└── .gitignore

Maven automatically looks for source files in these directories. If you follow conventions, you don't need to configure paths.

Creating a Maven Project

Use Maven Archetype to generate a project template. Archetypes are project templates with standard structure already set up.

# Create a simple Java project
mvn archetype:generate \
  -DgroupId=com.example \
  -DartifactId=my-app \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

# Create a web application (WAR)
mvn archetype:generate \
  -DgroupId=com.example \
  -DartifactId=my-webapp \
  -DarchetypeArtifactId=maven-archetype-webapp \
  -DinteractiveMode=false

This generates a complete project structure with pom.xml and sample code.

Building and Testing with Maven

Maven's build process follows phases. Each phase executes previous phases first.

// Sample source file: src/main/java/com/example/Calculator.java
package com.example;

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public int subtract(int a, int b) {
        return a - b;
    }
}

// Sample test file: src/test/java/com/example/CalculatorTest.java
package com.example;

import org.junit.Test;
import static org.junit.Assert.*;

public class CalculatorTest {
    @Test
    public void testAdd() {
        Calculator calc = new Calculator();
        assertEquals(5, calc.add(2, 3));
    }
    
    @Test
    public void testSubtract() {
        Calculator calc = new Calculator();
        assertEquals(1, calc.subtract(3, 2));
    }
}
# Compile source code (target/classes/)
mvn compile

# Compile test code (target/test-classes/)
mvn test-compile

# Run all tests
mvn test

# Run specific test
mvn test -Dtest=CalculatorTest

# Package as JAR
mvn package

# View generated JAR
jar tf target/my-app-1.0.0.jar

# Run packaged JAR
java -jar target/my-app-1.0.0.jar

Plugins and Custom Build Tasks

Maven plugins extend functionality. Configure plugins in pom.xml to add custom build steps. Common plugins: compiler, surefire (tests), assembly (packaging), shade (fat JARs), javadoc.

<build>
    <plugins>
        <!-- Compiler plugin with Java 11 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>11</source>
                <target>11</target>
            </configuration>
        </plugin>
        
        <!-- Surefire plugin (runs tests) -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
            <configuration>
                <includes>
                    <include>**/*Test.java</include>
                    <include>**/*Tests.java</include>
                </includes>
            </configuration>
        </plugin>
        
        <!-- Assembly plugin (create fat JAR with all dependencies) -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.example.App</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        
        <!-- Generate Javadoc -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-javadoc-plugin</artifactId>
            <version>3.3.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Maven Profiles for Environment-Specific Builds

Use profiles to have different configurations for development, testing, and production environments. Activate profiles via command line or based on environment conditions.

<profiles>
    <!-- Development profile -->
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <environment>development</environment>
            <database.url>jdbc:mysql://localhost:3306/dev_db</database.url>
            <debug>true</debug>
        </properties>
    </profile>
    
    <!-- Production profile -->
    <profile>
        <id>prod</id>
        <properties>
            <environment>production</environment>
            <database.url>jdbc:mysql://prod-server:3306/myapp</database.url>
            <debug>false</debug>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>3.2.4</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>
# Build with dev profile (default)
mvn clean package

# Build with prod profile
mvn clean package -Pprod

# List active profiles
mvn help:active-profiles
Maven Lifecycle PhasePurposeOutput
validateCheck project structureErrors if invalid
compileCompile source codetarget/classes/
testRun teststarget/test-reports/
packageCreate JAR/WARtarget/.jar or target/.war
installCopy to local repository~/.m2/repository/
deployUpload to remote repositoryRemote server

Common Mistake: Checking compiled target/ directory and JAR files into git. These are generated during build and should be in .gitignore. Also, avoid version conflicts by using <dependencyManagement> in parent POMs. Don't hardcode URLs/passwords in pom.xml — use Maven settings.xml or environment variables.

Best Practices for Maven Structure

  • Follow standard directory layout (src/main/java, src/test/java)
  • Place resources in src/main/resources and src/test/resources
  • Use profiles for environment-specific configuration
  • Keep pom.xml clean: extract repeated configurations into parent POM
  • Version your dependencies explicitly to avoid surprise updates
  • Use .gitignore to exclude target/, .classpath, IDE files
  • Maintain README.md with build and run instructions
  • Use meaningful artifact IDs that reflect project purpose

Real-World Example: Spring Boot Project Structure

A Spring Boot microservice project follows Maven conventions: src/main/java contains @SpringBootApplication and @RestController classes; src/main/resources contains application.yml with database URL (substituted from Maven profiles); src/test/java contains JUnit tests with @SpringBootTest. The pom.xml uses parent spring-boot-starter-parent for version management. Maven profiles define dev (H2 in-memory database) and prod (MySQL connection) settings. Running mvn clean package compiles, tests, and creates an executable JAR. Running with -Pprod uses production profile with real database.

Quiz Check

Quick Knowledge Test

Loading quiz questions...