Maven builds a project from its POM, lifecycle phases, dependency coordinates, and plugin configuration rather than from an IDE’s private settings.
Java Maven builds: compiler release and dependency scope
Java 21+. Use a JDK that supports this release.
Make the compiler contract visible
Running Maven on Java 21 does not mean the artifact should require Java 21. Set maven.compiler.release to the intended target. The compiler then checks syntax and platform APIs for that release, not just bytecode format.
Keep application sources in src/main/java and tests in src/test/java. A clean command-line build catches missing dependencies that an IDE may have cached or supplied through a local configuration.
The project below deliberately targets Java 21 because the modern examples use that release. A Java 8 application should choose release 8 and omit APIs unavailable there. See pattern switch for a feature that requires 21.
Separate test and runtime needs
JUnit belongs in test scope for this application. The H2 driver belongs at runtime because JDBC source imports standard Java types while DriverManager still needs the driver when connecting. A dependency scope is a packaging and classpath decision, not a security boundary.
Pin plugin and dependency versions so a review can reproduce the intended build inputs. The following versions are the tested lesson configuration, not a claim to be the newest releases. Update pins through a tested change.
Use mvn test for checks and mvn package for the artifact phase. A normal JAR does not automatically contain every dependency or an executable main entry. Distribution packaging is a separate requirement.
pom.xml
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>in.aitrove</groupId><artifactId>java-lesson-checks</artifactId><version>1.0.0</version>
<properties><maven.compiler.release>21</maven.compiler.release><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties>
<dependencies>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.11.0</version><scope>test</scope></dependency>
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>2.3.232</version><scope>runtime</scope></dependency>
</dependencies>
<build><plugins>
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.13.0</version></plugin>
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.5.2</version></plugin>
</plugins></build>
</project>Run from the project directory
mvn test
mvn packageCost and design choices
A first build downloads artifacts and plugins; later builds can use the local repository. Cache size and network latency affect build time, so one warm local result is not a portable build-time estimate.
Commit source and build definitions, not generated target output. A clean checkout should be able to reconstruct the artifact from declared inputs. Credentials belong outside the POM.
Common Mistakes
- Do not depend on an undeclared IDE classpath.
- Do not confuse the JDK running Maven with the compiler release.
- Do not claim a normal JAR bundles all dependencies.
Connect the contracts
Compare the boundary explained in Runtime database driver with the assumptions made by this program.
Apply this contract in Spring
Spring Boot application bootstrap: package boundaries and startup failures, Spring Boot receipt API project: build, test and inspect the limits. These lessons keep framework assembly separate from the Java contract.
