W3docs

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 transitiveri-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 una java.sql.Connection obbliga i chiamanti a vedere java.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 membri private) 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.

java— editable, runs on the server

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 un module-info.java — la prova che il file è puro metadato, non codice eseguibile.
  • Il require di java.sql viene stampato con il modificatore [TRANSITIVE] mentre com.acme.common viene stampato senza. Quel modificatore è ciò che ri-esporta java.sql ai moduli a valle; il require semplice mantiene la dipendenza privata a questo modulo.
  • I due export vengono stampati diversamente: com.acme.orders.api come "(to all)" e com.acme.orders.spi come "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.
  • opens compare nella sua sezione separata da exports. Il descrittore mantiene l'esposizione a tempo di compilazione e l'accesso riflessivo a runtime come fatti distinti, ecco perché un serializzatore ha bisogno di opens anche quando il pacchetto è già esportato.
  • uses e provides sono 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 un ServiceLoader funzionante.

Errori comuni

  • Inserire module-info.java all'interno di una directory di pacchetto — deve trovarsi alla radice sorgente.
  • Confondere exports (a tempo di compilazione, membri pubblici) con opens (a runtime, tutti i membri). Una JsonMappingException riguardante un campo inaccessibile significa quasi sempre un opens mancante.
  • Dimenticare requires transitive quando 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.

Esercitati

Pratica
Un modulo 'com.acme.orders' ha un metodo pubblico che restituisce una 'java.sql.Connection'. I chiamanti in altri moduli continuano a non riuscire a compilare perché non riescono a vedere il tipo 'java.sql.Connection', anche se hanno già 'requires com.acme.orders'. Quale direttiva in 'com.acme.orders' risolve questo problema senza costringere ogni chiamante ad aggiungere 'requires java.sql' autonomamente?
Un modulo 'com.acme.orders' ha un metodo pubblico che restituisce una 'java.sql.Connection'. I chiamanti in altri moduli continuano a non riuscire a compilare perché non riescono a vedere il tipo 'java.sql.Connection', anche se hanno già 'requires com.acme.orders'. Quale direttiva in 'com.acme.orders' risolve questo problema senza costringere ogni chiamante ad aggiungere 'requires java.sql' autonomamente?
Was this page helpful?