CodeGym /Kurse /Java Server /application Plugin und mainClass

application Plugin und mainClass

Java Server
Level 4, Lektion 2
Verfügbar

1. Gradle und der Einstiegspunkt main()

Zu diesem Zeitpunkt haben wir bereits Java-Code, Ressourcen und eine funktionierende ReadLaterApplication, die banner.txt aus dem classpath liest. Für Gradle ist das bisher aber einfach ein Java-Projekt. Damit die Aufgabe run überhaupt erscheint und stabil ein und dasselbe main() startet, muss das Projekt ausdrücklich als Anwendung deklariert und der Einstiegspunkt benannt werden.

In Java ist der Einstiegspunkt die Methode public static void main(String[] args). Aber in einem Projekt kann es mehrere Klassen mit main() geben (besonders bei Lernexperimenten), und Gradle ist nicht verpflichtet zu raten, welche davon „die Hauptklasse" ist. Deshalb machen wir die Wahl explizit: Wir wählen eine Einstiegsklasse und legen sie in build.gradle.kts fest.

2. Rollen der Plugins java und application

Bisher hatten wir bereits das Plugin java, das für Kompilierung, Quellcodestruktur und typische Aufgaben wie compileJava und classes zuständig ist. Aber das Plugin java selbst sagt Gradle nicht: „Hey, das ist eine Anwendung, die als Programm gestartet werden soll." Es geht eher darum, „Code zu bauen".

Das Plugin application verleiht dem Projekt das „Verhalten einer Anwendung": Es bringt die Aufgabe run und die dazugehörige Konfiguration. Das heißt, es ändert dein Java-Programm nicht magisch und fügt kein main() hinzu — das schreibst du nach wie vor selbst. Es gibt Gradle lediglich die Möglichkeit, das Programm korrekt über die JVM zu starten: den richtigen Classpath beim Start zusammenzustellen (einschließlich Abhängigkeiten) und das richtige main() aufzurufen.

Um das in ein Bild zu fassen, hilft folgende Mini-Tabelle:

Frage java Plugin application Plugin
„Wo liegt Code und Ressourcen?" Ja, legt die Standardstruktur src/main/java und src/main/resources fest Darum geht es nicht
„Wie kompilieren?" Ja (compileJava, classes) Nutzt indirekt das Kompilierungsergebnis
„Wie als Programm starten?" Nein (die IDE kann, Gradle muss nicht) Ja (run)
„Welches main() starten?" Löst das nicht Löst das über mainClass

Man kann sich das wie Rollen in einem Orchester vorstellen. Das Plugin java ist die Person, die die Bühne vorbereitet: Notenständer aufstellt, Noten druckt, Instrumente verteilt. Das Plugin application ist der Dirigent, der sagt: „Wir spielen jetzt dieses Stück und beginnen mit diesem Part." Und mainClass ist die „erste Geige": der konkrete Einstiegspunkt, an dem alles beginnt.

Noch ein wichtiger Punkt, den Einsteiger oft verwechseln: Das Plugin application hat nichts mit der Datei application.properties zu tun (das ist eine ganz andere Geschichte). Hier bedeutet „application" einfach „ausführbares Programm".

3. mainClass und der vollständige Klassenname

mainClass ist eine Gradle-Einstellung, die angibt, welche Klasse bei der Aufgabe run gestartet werden soll. Und hier beginnt die typische Verwirrung: In mainClass steht weder ein „Dateiname" noch ein „Pfad im Dateisystem", sondern der vollständige Java-Klassenname, also Name zusammen mit dem Paket.

Wenn zum Beispiel die Datei so liegt:

src/main/java/com/example/readlater
└─ ReadLaterApplication.java

und darin in der ersten Zeile steht:

package com.example.readlater;

ist der vollständige Klassenname:

com.example.readlater.ReadLaterApplication

Das ist kein Kosmetikproblem, sondern eine strenge Regel. Java-Pakete sind „Adressen" im Namensraum. Der Kurzname ReadLaterApplication ist wie der Name „Max" in einem Büro mit 200 Leuten: Technisch existiert der Name, aber versuch mal herauszufinden, welcher Max gemeint ist. Der vollständige Name com.example.readlater.ReadLaterApplication ist schon eher: „Max Müller aus dem Backend-Kurs, Tisch rechts neben der Kaffeemaschine." Die Fehlerquote sinkt rapide.

Wichtig ist auch, den Unterschied zwischen drei ähnlichen, aber verschiedenen Dingen zu verstehen:

Was es ist Beispiel Wo es verwendet wird
Kurzname der Klasse ReadLaterApplication Im Code, wenn die Klasse im selben Paket liegt oder importiert wurde
Vollständiger Klassenname (FQCN) com.example.readlater.ReadLaterApplication In der Einstellung mainClass, in JVM-Logs, in ClassNotFound-Fehlern
Dateipfad src/main/java/com/example/readlater/ReadLaterApplication.java Im Dateisystem des Projekts, in der IDE

Wenn man verwechselt und in mainClass einen Dateipfad eingibt, wird Gradle nicht „raten, was gemeint war". Es versucht ehrlich, eine Klasse mit diesem Namen zu starten, und schlägt fehl. Und das ist genau der Fall, wo der Fehler beängstigend aussieht, aber sich mit einem Buchstaben oder einem Punkt im Paketnamen beheben lässt.

4. ReadLaterApplication: der Einstiegspunkt

Eine gute Projektgewohnheit ist es, sich auf eine Haupt-Einstiegsklasse mit einem vorhersehbaren Namen zu einigen. Im Projekt ReadLater Starter legen wir die Klasse ReadLaterApplication fest. Das ist nicht „der einzig richtige Name im Universum", aber eine klare Vereinbarung: Wer das Projekt zum ersten Mal öffnet, findet schnell heraus, wo der Start ist.

Eine funktionierende Version dieser Klasse haben wir bereits: Sie liest banner.txt aus dem classpath. Jetzt ist es wichtig, main() nicht neu zu schreiben, sondern zwei Dinge zu beachten: das Paket der Klasse und die korrekte Signatur des Einstiegspunkts.

package com.example.readlater;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // hier bleibt die funktionierende Logik der Anwendung
    }
}

Achte auf drei Dinge, die oft am Auge vorbeigehen. Erstens muss main static sein, sonst kann die JVM ihn nicht aufrufen, ohne eine Objektinstanz zu erstellen. Zweitens muss die Signatur genau main(String[] args) sein (einige Variationen sind erlaubt, aber das brauchen wir jetzt nicht). Auch wenn die Argumente gerade nicht benötigt werden, bleibt die Signatur genau so: Die JVM schaut auf die Form des Einstiegspunkts, nicht auf den Reichtum der Innenlogik. Drittens muss das Paket mit dem Verzeichnis übereinstimmen, sonst kann die IDE den Start „reparieren", aber Gradle nicht.

5. build.gradle.kts: application und mainClass

Jetzt müssen wir diese Klasse nur noch mit der Gradle-Konfiguration verbinden. Nachdem wir ReadLaterApplication haben, muss Gradle wissen, dass genau diese Klasse der Einstiegspunkt ist. Das geschieht in build.gradle.kts durch das Einbinden des Plugins application und den Block application { ... }.

In Kotlin DSL sieht das maximal geradlinig aus. Das minimale Fragment, das wir brauchen, ist dieses:

plugins {
    java // Kompilierung, Standardprojektstruktur, Aufgaben wie compileJava/classes
    application // Fügt die Aufgabe run und die Konfiguration des Anwendungsstarts hinzu
}

application {
    // Vollständiger Klassenname (FQCN): Paket + Klassenname
    mainClass = "com.example.readlater.ReadLaterApplication"
}

Hier passieren gleich zwei wichtige Dinge. Das Einbinden von application fügt die Aufgabe run hinzu und macht das Projekt auf Gradle-Ebene „startbar". Und mainClass legt fest, welche Klasse gestartet werden soll. Der String "com.example.readlater.ReadLaterApplication" ist genau der vollständige Klassenname, über den wir gesprochen haben: Paket + Klassenname durch Punkte getrennt.

Vergisst man die Angabe von mainClass, kann Gradle run nicht korrekt ausführen und gibt einen Konfigurationsfehler aus. Hat man mainClass angegeben, aber den Paketnamen oder Klassennamen falsch geschrieben, kann der Build durchlaufen, aber der Start schlägt beim Versuch der JVM fehl, mit einer Meldung wie „Hauptklasse nicht gefunden". Und das ist normal: Kompilierung und Start sind verschiedene Lebensphasen eines Projekts.

6. Die Aufgabe run und der Start von main()

Nach dem Einbinden von application und der Angabe von mainClass erhält die Aufgabe run ein konkretes Ziel: com.example.readlater.ReadLaterApplication mit dem richtigen Classpath zur Laufzeit hochzufahren.

Der ehrlichste Weg zu prüfen, ob alles richtig konfiguriert ist, besteht darin, die Anwendung über Gradle zu starten:

./gradlew run # mainClass über Gradle starten (mit dem richtigen Runtime-Classpath)

Wenn alles richtig konfiguriert ist, siehst du die Ausgabe, die ReadLaterApplication aktuell produziert — momentan ist das das Banner aus banner.txt.

Es ist auch nützlich, einmal zu sehen, dass run wirklich mit der Vorbereitung von Klassen und Ressourcen verbunden ist. Gradle zeigt dann ungefähr folgende Aufgabenfolge:

> Task :compileJava
> Task :processResources
> Task :classes
> Task :run

Das ist eine gute Erinnerung: Das Plugin application kompiliert nicht und erstellt kein main() für dich. Es verbindet lediglich die bereits vorhandene Klasse mit der Start-Aufgabe.

Wenn man die Übergabe von Argumenten mechanisch überprüfen möchte, kann das Plugin application auch das:

./gradlew run --args="hello" # Argumente landen in String[] args deiner main()-Methode

Auch wenn die aktuelle Version von ReadLaterApplication sie noch nicht ausgibt, ist der Mechanismus derselbe: Gradle übergibt die Werte an String[] args.

Und seien wir ehrlich: Es ist nützlich, einmal eine typische Fehlfunktion zu sehen. Zum Beispiel, wenn man schreibt:

application {
    // Absichtlicher Tippfehler: Die JVM wird versuchen, eine Klasse mit diesem Namen zu finden, und scheitern
    mainClass = "com.example.readlater.ReadLaterApplicatoin"
}

dann schlägt ./gradlew run fehl, weil die JVM die Klasse nicht findet. Der Code kann wunderbar kompilieren, aber der Start schlägt trotzdem fehl: Du hast Gradle eine Adresse gegeben, unter der niemand wohnt.

7. Typische Fehler beim Arbeiten mit application und mainClass

In diesem Thema gibt es eine besondere Art von Fehlern: Sie sehen fast immer schlimmer aus, als sie sind. Das ist wie in eine Bank zu gehen und eine Schlange von 40 Leuten zu sehen: Man glaubt, verloren zu sein, und dann stellt sich heraus, dass die Hälfte der Leute einfach nach dem WLAN-Passwort fragt. Unten sind die häufigsten Stolperfallen für Einsteiger, und wie man sie an den Symptomen erkennt.

Fehler Nr. 1: In mainClass den Kurznamen der Klasse ohne Paket angeben.
Schreibt man mainClass = "ReadLaterApplication", findet Gradle die Klasse wahrscheinlich nicht, weil das für die JVM kein vollständiger Name ist. Manchmal hat man das Gefühl „aber die IDE sieht sie doch", aber die IDE ist hier kein Maßstab. Die einfache Strategie: ReadLaterApplication.java öffnen, die Zeile package ...; anschauen und paket + "." + Klassenname zusammensetzen.

Fehler Nr. 2: Den Klassennamen mit dem Dateipfad verwechseln.
Einsteiger versuchen manchmal etwas wie mainClass = "src/main/java/com/example/readlater/ReadLaterApplication.java" zu schreiben. Das ist menschlich logisch („da ist doch die Datei!"), für die JVM aber sinnlos: Es wird eine Klasse gestartet, keine Datei. In mainClass stehen immer Punkte, nie Schrägstriche, und niemals .java.

Fehler Nr. 3: Das Plugin application vergessen einzubinden und trotzdem run aufrufen wollen.
Wenn application nicht in plugins { ... } steht, erscheint die Aufgabe run einfach nicht. Das Symptom ist meistens: Gradle sagt, dass die Aufgabe run nicht existiert, oder schlägt etwas Ähnliches, aber nicht das Richtige vor. Die langweilige, aber wirksame Lösung: application zu plugins hinzufügen.

Fehler Nr. 4: Klasse vorhanden, mainClass angegeben, aber „Main method not found".
Das ist ein Klassiker, wenn main() falsch geschrieben ist. Zum Beispiel static vergessen, main(String args) statt String[] geschrieben oder die Methode versehentlich private gemacht. Die JVM sagt in diesem Fall ehrlich: Klasse gefunden, aber Einstiegspunkt — nicht. Die Lösung: Signatur prüfen: public static void main(String[] args).

Fehler Nr. 5: Paket im Code stimmt nicht mit dem Verzeichnis überein, und die IDE „unterhält die Illusion", während Gradle abstürzt.
Zum Beispiel liegt die Datei in src/main/java/com/example/readlater/ReadLaterApplication.java, aber innen steht package com.example;. Die IDE kann starten, weil sie eigene interne Mechanismen und Caches hat, aber Gradle baut nach den Regeln. Das ist unangenehm, weil es wie „Magie" aussieht. Die Lösung ist Disziplin: Verzeichnis und package müssen übereinstimmen, und mainClass muss mit beiden übereinstimmen.

1
Aufgabe
Java Server, Level 4, Lektion 2
Gesperrt
Expliziter Einstiegspunkt für LibraryApplication
Expliziter Einstiegspunkt für LibraryApplication
1
Aufgabe
Java Server, Level 4, Lektion 2
Gesperrt
Ein Einstiegspunkt unter zwei main-Klassen
Ein Einstiegspunkt unter zwei main-Klassen
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION