W3docs

Tipi di modulo Java

Moduli named, automatic e unnamed in Java e come interagiscono durante la compilazione e l'esecuzione.

Il Java Platform Module System (JPMS) riconosce tre tipi di modulo. Solo uno è il "vero" modulo che si scrive; gli altri due esistono affinché i milioni di JAR precedenti a Java 9 continuino a funzionare. Capire quale tipo diventa un determinato JAR — e che dipende interamente da dove lo si posiziona — è la chiave per migrare senza problemi. Questa pagina definisce tutti e tre i tipi, mostra le regole di accesso tra di essi e dimostra le categorie con un programma eseguibile.

Moduli named

Un modulo named (esplicito) è quello con un module-info.class, posizionato sul module path (--module-path / -p). È il cittadino di prima classe:

  • Ha un nome proveniente dal suo descrittore.
  • Legge solo i moduli che dichiara con requires.
  • Espone solo i pacchetti che dichiara con exports.

Questo è il modulo fortemente incapsulato descritto nel capitolo sulla dichiarazione del modulo. Tutto ciò che JPMS promette — dipendenze dichiarate, interni nascosti, risoluzione fail-fast — si applica ai moduli named.

Moduli automatic

Un modulo automatic è un JAR semplice (senza module-info) posizionato sul module path. JPMS lo racchiude in un modulo affinché i moduli named possano dichiararlo con requires durante la migrazione — senza dover attendere che l'autore della libreria aggiunga un descrittore. Un modulo automatic:

  • Ottiene un nome derivato dal nome del file JAR (es. guava-32.1.jarguava), a meno che il manifest del JAR non imposti Automatic-Module-Name.
  • Esporta ogni pacchetto — non ha direttiva exports, quindi tutti i suoi pacchetti sono aperti a tutti.
  • Legge ogni altro modulo, incluso il modulo unnamed, così può ancora vedere i JAR sul classpath.

È un ponte: permette di iniziare a scrivere moduli named che dipendono da librerie non ancora modularizzate. Il costo è che rinuncia completamente all'incapsulamento, e il suo nome derivato automaticamente può cambiare se il JAR viene rinominato — ecco perché Automatic-Module-Name nel manifest è la scelta responsabile per una libreria.

Il modulo unnamed

Il modulo unnamed è il raccoglitore universale per il classpath. Ogni classe caricata dal classpath appartiene al modulo unnamed del suo class loader. Esso:

  • Non ha nome (getName() restituisce null, isNamed() è false).
  • Legge ogni altro modulo nel sistema.
  • Esporta tutti i suoi pacchetti agli altri moduli unnamed/automatic.

Esiste però un muro deliberatamente unidirezionale: un modulo named non può dichiarare requires per il modulo unnamed. Non è possibile nominarlo, quindi non è possibile dipendere da esso. Questa è la regola che impone l'ordine di migrazione — un modulo named può dipendere solo da altri moduli named o automatic, mai da codice sul classpath grezzo.

La matrice di accesso

Chi può leggere chi si riduce a una piccola tabella:

Da ↓ / A →NamedAutomaticUnnamed
Namedsolo con requiressolo con requiresmai
Automatic
Unnamed

L'unica cella restrittiva — il codice named non può raggiungere il codice unnamed — è l'intera ragione per cui si migra dal basso verso l'alto (trattato nel prossimo capitolo).

Un esempio pratico: identificare il tipo di un modulo a runtime

La Module API indica, per qualsiasi classe, se il suo modulo è named e se è stato sintetizzato automaticamente. Questo programma ispeziona tre riferimenti — la propria classe (classpath → unnamed), un tipo JDK (named), e riporta il boot layer — per rendere concreti i concetti.

java— editable, runs on the server

Cosa ricavare dall'esecuzione:

  • La classe del programma è classificata come UNNAMED con nome null, mentre java.util.List e HttpClient sono classificati come NAMED (java.base, java.net.http). Eseguito dal classpath, il tuo codice è sempre unnamed; il JDK è sempre un insieme di moduli named. Il tipo di un modulo è determinato da come è stato caricato, non da nulla nella classe stessa.
  • java.base.canRead(self) ha restituito false ma self.canRead(java.base) ha restituito true. Questo è il muro unidirezionale in azione: il modulo unnamed legge tutto, ma nessun modulo named legge il modulo unnamed. Questa asimmetria è precisamente il motivo per cui il codice named non può dichiarare requires per il codice classpath.
  • classify() ha distinto automatic da named tramite descriptor.isAutomatic(). Non si vedrà true qui (nulla è stato posizionato sul module path come JAR semplice), ma il controllo è esattamente il modo in cui gli strumenti segnalano un modulo automatic — un vero oggetto modulo con un descrittore sintetizzato e completamente aperto.
  • isExported("java.util") ha restituito true ma isExported("jdk.internal.misc") ha restituito false, anche se entrambi sono pacchetti reali all'interno di java.base. La direttiva exports di un modulo named è una lista di permessi; i pacchetti non esportati (o esportati solo in modo qualificato) sono invisibili al codice esterno anche se public. Il modulo unnamed, al contrario, esporta tutto ciò che contiene.
  • Non è stato necessario alcun module-info.java per osservare nulla di tutto questo. Le tre categorie sono fatti a runtime sul modo in cui una classe è stata caricata, e getModule() più getDescriptor() le espongono — le stesse chiamate su cui si basano gli strumenti di migrazione per capire con cosa stanno lavorando.

Perché esistono tre tipi

I due tipi di compatibilità — automatic e unnamed — fanno sì che Java 9+ esegua applicazioni Java 8 non modificate. Si opta per un forte incapsulamento un JAR alla volta: si lascia tutto sul classpath (tutto unnamed) e niente cambia; si sposta una libreria sul module path senza un descrittore e diventa automatic; si aggiunge un module-info.java e diventa named. Successivamente, i servizi del modulo mostrano il meccanismo uses/provides che disaccoppia i moduli, e la migrazione dei moduli guida un progetto reale attraverso questi tre stati.

Esercizio

Pratica
Durante la migrazione, si posiziona un JAR di terze parti semplice (senza 'module-info.class') di nome 'fastjson-2.0.jar' sul MODULE PATH, poi si scrive 'requires fastjson;' nel proprio modulo named. Quale affermazione descrive correttamente 'fastjson' in questo caso?
Durante la migrazione, si posiziona un JAR di terze parti semplice (senza 'module-info.class') di nome 'fastjson-2.0.jar' sul MODULE PATH, poi si scrive 'requires fastjson;' nel proprio modulo named. Quale affermazione descrive correttamente 'fastjson' in questo caso?
Was this page helpful?