W3docs

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 transitivere-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 eine java.sql.Connection zurückgibt, zwingt Aufrufer, java.sql zu 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 auf private-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.

java— editable, runs on the server

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 einer module-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ährend com.acme.common ohne gedruckt wurde. Dieser Modifikator ist es, der java.sql an 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.api als „(to all)" und com.acme.orders.spi als „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.
  • opens erschien in einem eigenen Abschnitt, getrennt von exports. Der Deskriptor hält Kompilierungszeit-Zugang und reflektiven Laufzeitzugriff als getrennte Fakten, weshalb ein Serialisierer opens benötigt, selbst wenn das Paket bereits exportiert ist.
  • uses und provides sind 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 funktionierenden ServiceLoader.

Häufige Fehler

  • module-info.java in ein Paketverzeichnis legen — sie muss im Quellstamm sitzen.
  • exports (Kompilierungszeit, öffentliche Mitglieder) mit opens (Laufzeit, alle Mitglieder) verwechseln. Eine JsonMappingException über ein nicht zugängliches Feld bedeutet fast immer ein fehlendes opens.
  • requires transitive vergessen, 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.

Übungen

Übung
Ein Modul 'com.acme.orders' hat eine öffentliche Methode, die eine 'java.sql.Connection' zurückgibt. Aufrufer in anderen Modulen schlagen beim Kompilieren fehl, weil sie den Typ 'java.sql.Connection' nicht sehen können, obwohl sie bereits 'requires com.acme.orders' haben. Welche Direktive in 'com.acme.orders' behebt dies, ohne jeden Aufrufer zu zwingen, 'requires java.sql' selbst hinzuzufügen?
Ein Modul 'com.acme.orders' hat eine öffentliche Methode, die eine 'java.sql.Connection' zurückgibt. Aufrufer in anderen Modulen schlagen beim Kompilieren fehl, weil sie den Typ 'java.sql.Connection' nicht sehen können, obwohl sie bereits 'requires com.acme.orders' haben. Welche Direktive in 'com.acme.orders' behebt dies, ohne jeden Aufrufer zu zwingen, 'requires java.sql' selbst hinzuzufügen?
Was this page helpful?