Dichiarazione del modulo Java module-info.java
Dichiara un modulo Java con module-info.java: requires, exports, opens, uses, provides.
Un modulo viene dichiarato in un unico file sorgente speciale, module-info.java, posizionato alla radice dell'albero sorgente del modulo (accanto al pacchetto principale, non al suo interno). Viene compilato in module-info.class. Il file non contiene codice ordinario — solo un blocco module che elenca le direttive che descrivono il confine del modulo.
La struttura del file
module com.acme.orders {
requires com.acme.common; // I depend on this module
requires transitive java.sql; // ...and so does anyone who requires me
exports com.acme.orders.api; // public to everyone
exports com.acme.orders.spi to com.acme.web; // public to one module only
opens com.acme.orders.model; // deep reflective access (e.g. for Jackson)
uses com.acme.orders.PricingRule; // I consume this service
provides com.acme.orders.PricingRule // I supply an implementation
with com.acme.orders.StandardPricing;
}Il nome del modulo (com.acme.orders) è un identificatore puntato, convenzionalmente il prefisso DNS inverso dei pacchetti che contiene. Non è un pacchetto — è il proprio spazio dei nomi, e due moduli non possono condividere un pacchetto.
requires — dichiarare le dipendenze
requires <module> significa "ho bisogno dei pacchetti esportati di quel modulo per compilare ed eseguire." Il resolver fallisce all'avvio se un modulo richiesto è assente. Due modificatori importanti:
requires transitive— ri-esporta la dipendenza. Qualsiasi modulo che richiede il tuo modulo la legge automaticamente. Usalo quando i tipi di un modulo richiesto compaiono nelle tue stesse firme pubbliche (un metodo che restituisce unajava.sql.Connectionobbliga i chiamanti a vederejava.sql).requires static— una dipendenza solo a tempo di compilazione, opzionale a runtime (per i processori di annotazioni, le integrazioni opzionali).
java.base è richiesto implicitamente; non è necessario scriverlo.
exports — dichiarare la tua API pubblica
exports <package> rende i tipi public di quel pacchetto visibili agli altri moduli. Tutto ciò che non è esportato è fortemente incapsulato — invisibile anche se public. La forma qualificata, exports <package> to <module>, <module>, restringe la visibilità a un elenco di moduli nominati, utile per i pacchetti SPI condivisi solo tra i propri moduli.
Nota che exports è per pacchetto, mai ricorsivo: esportare com.acme.api non esporta com.acme.api.internal.
opens — consentire la riflessione profonda
exports concede l'accesso a tempo di compilazione ai membri public. Non concede l'accesso riflessivo ai membri non pubblici. Framework come Jackson, Hibernate e Spring usano setAccessible(true) per accedere ai campi private — questo richiede opens:
opens <package>— concede l'accesso riflessivo a runtime (inclusi i membriprivate) a tutti i moduli.opens <package> to <module>— qualificato, solo ai moduli nominati.open module com.acme.orders { … }— apre ogni pacchetto (un aiuto grezzo per la migrazione).
La distinzione è importante: si usa exports per un pacchetto API, ma opens per un pacchetto di classi dati che si vuole che un serializzatore possa ispezionare tramite riflessione senza renderlo parte dell'API a tempo di compilazione.
uses / provides — servizi
Queste direttive collegano il pattern ServiceLoader: uses <Service> dichiara che si consuma un'interfaccia di servizio, e provides <Service> with <Impl> dichiara un'implementazione. Hanno un capitolo dedicato — vedi Servizi del modulo — qui basta notare che risiedono nello stesso descrittore.
Un esempio pratico: costruire un descrittore con l'API
Normalmente si scrive module-info.java e si lascia che javac produca il descrittore. Ma la stessa struttura è disponibile a livello programmatico tramite ModuleDescriptor.newModule(...), che è uno specchio fedele della sintassi delle direttive — costruirne uno è il modo più chiaro per vedere cosa diventa ciascuna direttiva.
Cosa osservare dall'esecuzione:
- I nomi dei metodi del builder corrispondono uno a uno con le direttive:
.requires(...),.exports(...),.opens(...),.uses(...),.provides(...). Leggendo l'output, il descrittore è esattamente le informazioni presenti in unmodule-info.java— la prova che il file è puro metadato, non codice eseguibile. - Il
requiredijava.sqlviene stampato con il modificatore[TRANSITIVE]mentrecom.acme.commonviene stampato senza. Quel modificatore è ciò che ri-esportajava.sqlai moduli a valle; il require semplice mantiene la dipendenza privata a questo modulo. - I due export vengono stampati diversamente:
com.acme.orders.apicome "(to all)" ecom.acme.orders.spicome "to [com.acme.web]". Un export qualificato porta il proprio elenco di destinatari all'interno del descrittore — il resolver lo applica, quindi nessun altro modulo può leggere il pacchetto SPI. openscompare nella sua sezione separata daexports. Il descrittore mantiene l'esposizione a tempo di compilazione e l'accesso riflessivo a runtime come fatti distinti, ecco perché un serializzatore ha bisogno diopensanche quando il pacchetto è già esportato.useseprovidessono registrati insieme al resto — le dichiarazioni di servizio fanno parte del confine del modulo, non un file di configurazione separato come avveniva sul classpath (META-INF/services). Servizi del modulo trasforma queste direttive in unServiceLoaderfunzionante.
Errori comuni
- Inserire
module-info.javaall'interno di una directory di pacchetto — deve trovarsi alla radice sorgente. - Confondere
exports(a tempo di compilazione, membri pubblici) conopens(a runtime, tutti i membri). UnaJsonMappingExceptionriguardante un campo inaccessibile significa quasi sempre unopensmancante. - Dimenticare
requires transitivequando i propri metodi pubblici espongono tipi di un altro modulo, costringendo ogni chiamante ad aggiungere manualmente il require.
Il capitolo successivo, Tipi di modulo, torna ai tre tipi di modulo — named, automatic e unnamed — e a come un'applicazione semi-modularizzata continua a funzionare durante la migrazione ai moduli. Per il quadro generale del perché esiste il sistema di moduli, vedi l'introduzione ai moduli.