Java Maven pom.xml
Il Project Object Model di Maven — struttura di pom.xml, coordinate, proprietà ed ereditarietà.
Il pom.xml è il cuore di qualsiasi progetto Maven. POM sta per Project Object Model — un singolo file XML che dichiara cosa è il tuo progetto (la sua identità), di cosa ha bisogno (le sue dipendenze) e come costruirlo (plugin e configurazione). Maven legge questo file, scarica tutto ciò a cui fa riferimento da un repository ed esegue la build. Mentre un progetto improvvisato disperde queste informazioni tra script shell e una cartella lib/ piena di JAR copiati a mano, Maven le raccoglie tutte in un unico documento dichiarativo e sotto controllo di versione.
Questo capitolo percorre la struttura del POM pezzo per pezzo: lo scheletro minimale, le coordinate GAV che identificano ogni artefatto, come funzionano le dipendenze e gli scope, le proprietà per evitare duplicazioni di versioni e l'ereditarietà attraverso un POM genitore. Se sei nuovo a Maven, inizia con l'introduzione a Maven; per le fasi di build che il POM gestisce, consulta il ciclo di vita della build Maven.
Il POM minimale
Ogni POM è un documento XML con radice nell'elemento <project> con lo schema Maven 4.0.0. Il POM minimo funzionale dichiara la sua versione del modello e le sue coordinate — i quattro valori che identificano l'artefatto:
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.w3docs</groupId>
<artifactId>shop-api</artifactId>
<version>1.4.0</version>
<packaging>jar</packaging>
</project><modelVersion> è sempre 4.0.0 — è la versione del formato POM, non del tuo codice. Lasciala esattamente come mostrato.
Coordinate: groupId, artifactId, version
Un artefatto Maven è identificato dalle sue coordinate GAV. Ogni dipendenza che aggiungi viene nominata dagli stessi tre (a volte quattro) valori, quindi vale la pena impararli con precisione:
| Elemento | Significato | Convenzione |
|---|---|---|
groupId | L'organizzazione o il namespace | DNS inverso, es. com.google.code.gson |
artifactId | Il nome del progetto all'interno del gruppo | Minuscolo, con trattini, es. shop-api |
version | Il rilascio di questo artefatto | Semantica, es. 1.4.0; -SNAPSHOT per build in corso |
packaging | Il tipo di output | jar (predefinito), war, pom |
Insieme, groupId:artifactId:version è globalmente unico. È ciò che Maven usa per individuare un JAR in un repository e per nominare l'artefatto prodotto dalla build. Una versione che termina in -SNAPSHOT (es. 1.5.0-SNAPSHOT) è una build mutabile e in sviluppo che Maven potrebbe riscaricare; una versione senza questo suffisso è considerata immutabile.
Dichiarare le dipendenze
Le dipendenze vengono elencate sotto <dependencies>, ognuna come blocco <dependency> che fornisce le coordinate di una libreria necessaria. Maven le risolve — e le loro dipendenze, transitivamente — da un repository (Maven Central per impostazione predefinita):
<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.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>Lo <scope> controlla quando una dipendenza è nel classpath. compile (il valore predefinito) significa ovunque; test significa solo durante la compilazione e l'esecuzione dei test, quindi JUnit non viene mai incluso nel JAR. Altri scope includono provided (fornito dal runtime, es. una servlet API) e runtime (necessario per l'esecuzione ma non per la compilazione, es. un driver JDBC). La risoluzione transitiva, i conflitti di versione e le regole degli scope sono trattati in dettaglio in dipendenze Maven.
Proprietà: non ripetere le versioni
L'elemento <properties> definisce variabili riutilizzabili, referenziate altrove con la sintassi ${name}. L'idioma consiste nel fissare ogni versione di libreria una volta sola in cima, così gli aggiornamenti avvengono in un unico punto:
<properties>
<maven.compiler.release>21</maven.compiler.release>
<junit.version>5.10.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>maven.compiler.release è una proprietà ben nota che indica al plugin del compilatore quale versione di Java usare come target — più pulita rispetto alla configurazione manuale del plugin. Quando Maven legge il POM, interpola ogni segnaposto ${...}, sostituendo il valore della proprietà prima di risolvere qualsiasi cosa.
Ereditarietà con un POM genitore
I POM formano una gerarchia. Un elemento <parent> fa ereditare a un progetto le coordinate, le proprietà e la gestione delle dipendenze da un altro POM. Il parent starter di Spring Boot è l'esempio classico — fissa un insieme coerente di versioni di librerie in modo da poter omettere <version> per le dipendenze gestite:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
</parent>
<dependencies>
<dependency>
<!-- version omitted: inherited from the parent's dependencyManagement -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>Una build multi-modulo usa lo stesso meccanismo in modo inverso: un POM aggregatore con <packaging>pom</packaging> elenca i <modules>, e ogni figlio lo nomina come <parent>. La configurazione condivisa vive in un unico posto; i figli rimangono snelli.
Un esempio pratico: leggere un POM come farebbe Maven
Un pom.xml è semplice XML, quindi possiamo analizzarne uno con il DOM API integrato del JDK e percorrerne la struttura esattamente come farebbe Maven — leggendo le coordinate, le proprietà e ciascuna dipendenza, interpolando a mano un segnaposto ${...}. (Maven stesso è uno strumento di build, non una libreria nel classpath qui, ma questo mostra il modello su cui opera.)
Cosa ricavare dall'esecuzione:
- La
modelVersionviene stampata come4.0.0— la costante che ogni POM contiene. Identifica il formato del POM, non il tuo progetto, e Maven rifiuterebbe un POM che la omette. - Le coordinate risultano
com.w3docs:shop-api:1.4.0, la tripla GAV nella sua forma canonicagroupId:artifactId:version. Questa singola stringa è il modo in cui Maven identifica sia l'artefatto prodotto dalla build sia ogni dipendenza che risolve. - Il
packagingviene letto comejar, il tipo di output predefinito. Se fosse statopom, questo sarebbe un progetto aggregatore/genitore che non produce alcun JAR proprio. - La proprietà
junit.versionsi è risolta in5.10.2, e il segnaposto${junit.version}della dipendenza JUnit è stato interpolato allo stesso valore — esattamente la sostituzione che Maven esegue affinché una versione sia dichiarata in un unico posto e riutilizzata. - La scansione delle dipendenze ha riportato due dipendenze e le ha stampate ciascuna con il proprio scope: Gson è impostato su
compileper impostazione predefinita (nessun elemento<scope>, quindi è in ogni classpath), mentre JUnit mostrascope=test, mantenendolo fuori dall'artefatto distribuito.