CodeGym /Kursy /Java Server /application plugin i mainClass

application plugin i mainClass

Java Server
Poziom 4, Lekcja 2
Dostępny

1. Gradle i punkt wejścia main()

W tym momencie mamy już kod Java, zasoby i działającą ReadLaterApplication, która czyta banner.txt z classpath. Ale dla Gradle to wciąż po prostu projekt Java. Żeby zadanie run w ogóle się pojawiło i stabilnie uruchamiało ten sam main(), projekt trzeba jawnie zadeklarować jako aplikację i nazwać punkt wejścia.

W Javie punkt wejścia – to metoda public static void main(String[] args). Ale w projekcie może być kilka klas z main() (szczególnie w eksperymentach edukacyjnych), a Gradle nie musi zgadywać, który z nich jest „głównym”. Dlatego dokonujemy wyboru jawnego: wybieramy jedną klasę-wejście i zapisujemy ją w build.gradle.kts.

2. Role wtyczek java i application

Do tego momentu mieliśmy już wtyczkę java, i ona odpowiada za kompilację, strukturę źródeł i typowe zadania w stylu compileJava i classes. Ale wtyczka java sama z siebie nie mówi Gradle: „Hej, to jest aplikacja, trzeba ją móc uruchamiać jak program”. Jest raczej o „zbudować kod”.

Wtyczka application właśnie dodaje projektowi „zachowanie aplikacji”: przynosi zadanie run i powiązaną z nim konfigurację. Czyli nie zmienia twojego programu Java magicznie i nie dodaje do niego main() – main() i tak piszesz ty. On po prostu daje Gradle możliwość prawidłowo uruchamiać program przez JVM: złożyć właściwy classpath przy uruchomieniu (włącznie z zależnościami) i wywołać potrzebny main().

Żeby to uložyć w jeden obraz, wygodnie trzymać w głowie taką mini-tabelę:

Pytanie java plugin application plugin
„Gdzie leży kod i zasoby?” Tak, zadaje standardową strukturę src/main/java i src/main/resources Nie o tym
„Jak kompilować?” Tak (compileJava, classes) Pośrednio używa wyniku kompilacji
„Jak uruchomić jako program?” Nie (IDE potrafi, Gradle – nie musi) Tak (run)
„Jak zrozumieć, który main() uruchamiać?” Nie rozstrzyga Rozstrzyga przez mainClass

Można to wyobrazić jako role w orkiestrze. java plugin – to człowiek, który przygotowuje scenę: stawia pulpity, drukuje nuty, rozdaje instrumenty. application plugin – dyrygent, który mówi: „Teraz gramy oto ten utwór i zaczynamy od tej partii”. A mainClass – ta sama „pierwsza skrzypce”: konkretny punkt, od którego wszystko się zaczyna.

I jeszcze jeden ważny moment, który nowicjusze często mylą: application plugin nie ma związku z plikiem application.properties (to całkiem inna historia). Tu „application” oznacza „uruchamiany program”.

3. mainClass i pełna nazwa klasy

mainClass – to ustawienie dla Gradle, które wskazuje, którą klasę trzeba uruchamiać w zadaniu run. I tu zaczyna się typowe zamieszanie: w mainClass pisze się nie „nazwę pliku” i nie „ścieżkę w systemie plików”, a pełną nazwę klasy Java, czyli nazwę razem z pakietem.

Na przykład, jeśli twój plik leży tak:

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

a wewnątrz niego w pierwszej linijce jest napisane:

package com.example.readlater;

to pełna nazwa klasy będzie:

com.example.readlater.ReadLaterApplication

To nie kosmetyka, a ścisła reguła. Pakiety Javy – to „adresy” w przestrzeni nazw. Krótka nazwa ReadLaterApplication jest jak imię „Adam” w biurze na 200 osób: technicznie imię jest, ale spróbuj zrozumieć, który Adam jest potrzebny. Pełna nazwa com.example.readlater.ReadLaterApplication – to już „Adam Kowalski z kursu backendowego, biurko z prawej, obok ekspresu do kawy”. Szanse na pomyłkę gwałtownie spadają.

Ważne też zrozumieć różnicę między trzema podobnymi, ale różnymi rzeczami:

Co to jest Przykład Gdzie używane
Krótka nazwa klasy ReadLaterApplication W kodzie, jeśli klasa jest w tym samym pakiecie lub zaimportowana
Pełna nazwa klasy (FQCN) com.example.readlater.ReadLaterApplication W ustawieniu mainClass, w logach JVM, w błędach ClassNotFound
Ścieżka do pliku src/main/java/com/example/readlater/ReadLaterApplication.java W systemie plików projektu, w IDE

Jeśli pomylisz i napiszesz w mainClass ścieżkę do pliku, Gradle nie będzie „zgadywał, co miałeś na myśli”. Uczciwie spróbuje uruchomić klasę o takiej nazwie i upadnie. I to właśnie ten przypadek, kiedy błąd wygląda strasznie, ale leczy się jedną literą lub jedną kropką w pakiecie.

4. ReadLaterApplication: punkt wejścia

Dobry nawyk dla projektu – umówić się o jednej głównej klasie wejścia z przewidywalną nazwą. W projekcie ReadLater Starter ustalamy klasę ReadLaterApplication. To nie jest „jedyna prawidłowa nazwa we wszechświecie”, ale zrozumiała umowa: jeśli ktoś otwiera projekt po raz pierwszy, szybko znajduje, gdzie jest start.

Robocza wersja tej klasy już u nas jest: czyta banner.txt z classpath. Teraz ważne nie przepisywać main() od nowa, a zauważyć dwie rzeczy: pakiet klasy i poprawną sygnaturę punktu wejścia.

package com.example.readlater;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // tutaj zostaje robocza logika aplikacji
    }
}

Zwróć uwagę na trzy rzeczy, które często prześlizgują się mimo oczu. Po pierwsze, main musi być static, inaczej JVM nie będzie mogła go wywołać bez instancji obiektu. Po drugie, sygnatura musi być właśnie main(String[] args) (para wariacji jest dopuszczalna, ale teraz nie jest nam to potrzebne). Nawet jeśli argumenty na razie nie są ci potrzebne, sygnatura pozostaje właśnie taka: JVM patrzy na formę punktu wejścia, a nie na bogactwo wewnętrznej logiki. Po trzecie, pakiet musi odpowiadać directory, inaczej IDE może „naprawić” uruchomienie, a Gradle – nie.

5. build.gradle.kts: application i mainClass

Teraz pozostaje powiązać tę klasę z konfiguracją Gradle. Po tym, jak mamy ReadLaterApplication, Gradle trzeba wyjaśnić, że właśnie ona jest punktem wejścia. Robi się to w build.gradle.kts przez podłączenie wtyczki application i blok application { ... }.

W Kotlin DSL wygląda to maksymalnie wprost. Minimalny fragment, którego potrzebujesz, taki:

plugins {
    java // Kompilacja, standardowa struktura projektu, zadania typu compileJava/classes
    application // Dodaje zadanie run i konfigurację uruchomienia aplikacji
}

application {
    // Pełna nazwa klasy (FQCN): pakiet + nazwa klasy
    mainClass = "com.example.readlater.ReadLaterApplication"
}

Tutaj dzieją się od razu dwie ważne rzeczy. Podłączenie application dodaje zadanie run i czyni projekt „uruchamialnym” na poziomie Gradle. A mainClass ustala, którą klasę trzeba uruchamiać. Linijka "com.example.readlater.ReadLaterApplication" – to ta sama pełna nazwa klasy, o której mówiliśmy: pakiet + nazwa klasy przez kropkę.

Jeśli zapomniałeś wskazać mainClass, Gradle nie będzie mógł poprawnie wykonać run i będzie się złościł błędem konfiguracyjnym. Jeśli wskazałeś mainClass, ale pomyliłeś się w pakiecie lub nazwie klasy, kompilacja może przejść, a uruchomienie upadnie już przy próbie startu JVM z komunikatem na poziomie „nie znaleziono głównej klasy”. I to jest normalne: kompilacja i uruchomienie – różne etapy życia projektu.

6. Zadanie run i uruchomienie main()

Po podłączeniu application i wskazaniu mainClass zadanie run dostaje konkretny cel: uruchomić com.example.readlater.ReadLaterApplication z poprawnym classpath przy uruchomieniu.

Najuczciwszy sposób sprawdzenia, że wszystko jest ustawione prawidłowo – uruchomić aplikację przez Gradle:

./gradlew run # Uruchomienie mainClass przez Gradle (z prawidłowym runtime classpath)

Jeśli wszystko jest ustawione prawidłowo, zobaczysz to wyjście, które robi obecny ReadLaterApplication – teraz to baner z banner.txt.

Również warto raz zobaczyć, że run faktycznie jest powiązany z przygotowaniem klas i zasobów. Gradle zwykle pokaże coś w stylu:

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

To dobre przypomnienie: application plugin nie kompiluje i nie tworzy main() za ciebie. Po prostu wiąże już istniejącą klasę z zadaniem uruchomienia.

Jeśli trzeba mechanicznie sprawdzić przekazywanie argumentów, application plugin potrafi i to:

./gradlew run --args="hello" # Argumenty trafią do String[] args twojego main()

Nawet jeśli bieżąca wersja ReadLaterApplication ich na razie nie drukuje, mechanizm jest już ten sam: Gradle przekaże wartości do String[] args.

I bądźmy szczerzy: warto raz zobaczyć typową awarię. Na przykład, jeśli napiszesz:

application {
    // Celowa literówka: JVM spróbuje znaleźć klasę o tej nazwie i nie znajdzie
    mainClass = "com.example.readlater.ReadLaterApplicatoin"
}

to ./gradlew run upadnie, ponieważ JVM nie znajdzie klasy. Tutaj kod może się świetnie kompilować, ale uruchomienie i tak się zepsuje: dałeś Gradle adres, pod którym nikt nie mieszka.

7. Typowe błędy przy pracy z application i mainClass

W tym temacie istnieje szczególny rodzaj błędów: niemal zawsze wyglądają strasznie niż są w rzeczywistości. To jak przyjść do banku i zobaczyć kolejkę 40 osób: wydaje się, że jesteś zgubiony, a potem okazuje się, że połowa ludzi po prostu pyta o hasło do Wi‑Fi. Poniżej – najczęstsze grabie, na które wchodzą nowicjusze, i jak je rozpoznać po objawach.

Błąd nr 1: wskazać w mainClass krótką nazwę klasy bez pakietu.
Jeśli napiszesz mainClass = "ReadLaterApplication", Gradle najprawdopodobniej nie znajdzie klasy, ponieważ dla JVM to nie jest pełna nazwa. Czasami wydaje się, że „no IDE widzi”, ale IDE nie jest tu wyznacznikiem. Prawidłowa strategia jest prosta: otworzyć ReadLaterApplication.java, spojrzeć na linijkę package ...; i skleić package + "." + nazwaKlasy.

Błąd nr 2: pomylić nazwę klasy i ścieżkę do pliku.
Nowicjusze czasami próbują napisać coś w stylu mainClass = "src/main/java/com/example/readlater/ReadLaterApplication.java". Jest to logiczne po ludzku („oto przecież plik!”), ale dla JVM bez sensu: uruchamia się klasa, a nie plik. W mainClass zawsze kropki, a nie ukośniki, i nigdy nie pisze się .java.

Błąd nr 3: zapomnieć podłączyć wtyczkę application i próbować uruchamiać run.
Jeśli w plugins { ... } nie ma application, zadanie run po prostu się nie pojawi. Objaw zwykle taki: Gradle mówi, że zadanie run nie istnieje, lub proponuje coś podobnego, ale nie to. Leczenie nudne, ale skuteczne: dodać application do plugins.

Błąd nr 4: klasa jest, mainClass wskazany, ale „Main method not found”.
To klasyka, kiedy main() napisany nieprawidłowo. Na przykład zapomniano static, napisano main(String args) zamiast String[] lub przypadkowo zrobiono metodę private. JVM w tym przypadku uczciwie mówi: klasę znalazła, a punktu wejścia – nie. Leczy się sprawdzeniem sygnatury: public static void main(String[] args).

Błąd nr 5: pakiet w kodzie nie odpowiada directory, a IDE „podtrzymuje iluzję”, a Gradle się łamie.
Na przykład plik leży w src/main/java/com/example/readlater/ReadLaterApplication.java, ale wewnątrz napisano package com.example;. IDE może uruchomić, ponieważ ma swoje wewnętrzne mechanizmy i cache, a Gradle będzie budował według reguł. To nieprzyjemne, ponieważ wygląda jak „magia”. Rozwiązanie – dyscyplina: directory i package muszą się pokrywać, a mainClass – pokrywać się z obydwoma.

1
Zadanie
Java Server, poziom 4, lekcja 2
Niedostępne
Jawny punkt wejścia dla LibraryApplication
Jawny punkt wejścia dla LibraryApplication
1
Zadanie
Java Server, poziom 4, lekcja 2
Niedostępne
Jeden punkt wejścia spośród dwóch klas main
Jeden punkt wejścia spośród dwóch klas main
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION