Java module-info.java Deklaration
Java-Modul mit module-info.java deklarieren — requires, exports, opens, uses, provides.
Ein Modul wird in einer speziellen Quelldatei deklariert, module-info.java, die im Stammverzeichnis des Modulquellbaums liegt (neben dem obersten Paket, nicht darin). Sie wird zu module-info.class kompiliert. Die Datei enthält keinen gewöhnlichen Code — nur einen module-Block mit Direktiven, die die Grenze des Moduls beschreiben.
Die Struktur der Datei
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;
}Der Name des Moduls (com.acme.orders) ist ein Bezeichner mit Punktnotation, üblicherweise das umgekehrte DNS-Präfix der enthaltenen Pakete. Er ist kein Paket — er ist ein eigener Namensraum, und zwei Module dürfen kein Paket teilen.
requires — Abhängigkeiten deklarieren
requires <module> bedeutet: „Ich benötige die exportierten Pakete dieses Moduls zum Kompilieren und Ausführen." Der Resolver schlägt beim Start fehl, wenn ein benötigtes Modul fehlt. Zwei wichtige Modifikatoren:
requires transitive— re-exportiert die Abhängigkeit. Jedes Modul, das dieses Modul benötigt, liest sie automatisch auch. Verwenden Sie es, wenn Typen eines abhängigen Moduls in Ihren eigenen öffentlichen Signaturen erscheinen (eine Methode, die einejava.sql.Connectionzurückgibt, zwingt Aufrufer,java.sqlzu sehen).requires static— eine nur zur Kompilierungszeit gültige Abhängigkeit, zur Laufzeit optional (für Annotationsprozessoren, optionale Integrationen).
java.base wird implizit benötigt; Sie schreiben es niemals.
exports — Ihre öffentliche API deklarieren
exports <package> macht die public-Typen dieses Pakets für andere Module sichtbar. Alles, was nicht exportiert wird, ist stark gekapselt — unsichtbar, auch wenn public. Die qualifizierte Form, exports <package> to <module>, <module>, schränkt die Sichtbarkeit auf eine benannte Zulassungsliste ein — nützlich für SPI-Pakete, die nur zwischen Ihren eigenen Modulen geteilt werden.
Beachten Sie, dass exports pro-Paket gilt und nicht rekursiv ist: das Exportieren von com.acme.api exportiert nicht com.acme.api.internal.
opens — tiefen Reflektionszugriff erlauben
exports gewährt Kompilierungszeit-Zugriff auf public-Mitglieder. Es gewährt keinen reflektiven Zugriff auf nicht-öffentliche Mitglieder. Frameworks wie Jackson, Hibernate und Spring verwenden setAccessible(true), um auf private-Felder zuzugreifen — dafür wird opens benötigt:
opens <package>— gewährt allen Modulen reflektiven Laufzeitzugriff (auch aufprivate-Mitglieder).opens <package> to <module>— qualifiziert, nur für benannte Module.open module com.acme.orders { … }— öffnet jedes Paket (ein pauschales Migrationshilfsmittel).
Die Unterscheidung ist wichtig: Sie exports-en ein API-Paket, aber Sie opens-en ein Paket mit Datenklassen, über die ein Serialisierer reflektieren soll, ohne es zu einem Teil Ihrer Kompilierungszeit-API zu machen.
uses / provides — Dienste
Diese verdrahten das ServiceLoader-Muster: uses <Service> erklärt, dass Sie eine Dienstschnittstelle konsumieren, und provides <Service> with <Impl> erklärt eine Implementierung. Sie haben ein eigenes Kapitel — siehe Moduldienste — hier ist nur anzumerken, dass sie im gleichen Deskriptor leben.
Ein durchgearbeitetes Beispiel: einen Deskriptor mit der API erstellen
Normalerweise schreiben Sie module-info.java und lassen javac den Deskriptor erzeugen. Dieselbe Struktur ist jedoch programmatisch über ModuleDescriptor.newModule(...) verfügbar, das die Direktivsyntax originalgetreu spiegelt — einen Deskriptor zu erstellen ist der deutlichste Weg, um zu sehen, was aus jeder Direktive wird.
Was aus der Ausführung zu entnehmen ist:
- Die Namen der Builder-Methoden stimmen eins zu eins mit den Direktiven überein:
.requires(...),.exports(...),.opens(...),.uses(...),.provides(...). Beim Lesen der Ausgabe ist der Deskriptor genau die Information in einermodule-info.java— Beweis dafür, dass die Datei reine Metadaten und kein ausführbarer Code ist. - Das
java.sql-Require wurde mit einem[TRANSITIVE]-Modifikator gedruckt, währendcom.acme.commonohne gedruckt wurde. Dieser Modifikator ist es, derjava.sqlan nachgelagerte Module re-exportiert; das einfache Require hält die Abhängigkeit für dieses Modul privat. - Die beiden Exports wurden unterschiedlich gedruckt:
com.acme.orders.apials „(to all)" undcom.acme.orders.spials „to [com.acme.web]". Ein qualifizierter Export trägt seine Zulassungsliste im Deskriptor — der Resolver erzwingt sie, sodass kein anderes Modul das SPI-Paket lesen kann. openserschien in einem eigenen Abschnitt, getrennt vonexports. Der Deskriptor hält Kompilierungszeit-Zugang und reflektiven Laufzeitzugriff als getrennte Fakten, weshalb ein Serialisiereropensbenötigt, selbst wenn das Paket bereits exportiert ist.usesundprovidessind direkt neben dem Rest aufgezeichnet — die Dienstdeklarationen sind Teil der Modulgrenze, keine separate Konfigurationsdatei wie auf dem Classpath (META-INF/services). Moduldienste verwandelt diese Direktiven in einen funktionierendenServiceLoader.
Häufige Fehler
module-info.javain ein Paketverzeichnis legen — sie muss im Quellstamm sitzen.exports(Kompilierungszeit, öffentliche Mitglieder) mitopens(Laufzeit, alle Mitglieder) verwechseln. EineJsonMappingExceptionüber ein nicht zugängliches Feld bedeutet fast immer ein fehlendesopens.requires transitivevergessen, wenn Ihre öffentlichen Methoden Typen eines anderen Moduls exponieren, was jeden Aufrufer zwingt, das Require manuell hinzuzufügen.
Das nächste Kapitel, Arten von Modulen, befasst sich mit den drei Arten von Modulen — benannte, automatische und unbenannte — und wie eine halb-modularisierte Anwendung weiterläuft, während Sie zu Modulen migrieren. Für das größere Bild, warum das Modulsystem existiert, siehe die Moduleinführung.