Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 2 - Classloader a první modul v Javě

V předchozí lekci, Úvod do modulů v Javě, jsme se seznámili se strukturou a vlastnostmi modulů a ukázali si, jak pomáhají řešit problematiku závislosti balíčků.

V této lekci budeme ještě chvilku pokračovat v teorii. V dnešním Java tutoriálu si povíme, jak funguje classloader a seznámíme se s různými typy modulů. Na závěr si ukážeme, jak takový modul vypadá.

Načítání tříd

O načítání tříd v aplikaci se stará tzv. classloader. V Javě existují celkem tři druhy classloaderů:

  • Bootstrap classloader - základní classloader, naimplementovaný přímo v C++ (nikoliv v Javě), starající se o načtení kritických modulů, jako je java.base,
  • Platform classloader - tento classloader je zodpovědný za načítání všech nekritických modulů, které ale jsou stále součástí JRE, jako je java.sql,
  • System (Application) classloader - poslední classloader načítá všechny ostatní moduly, typicky se jedná o námi vytvořené moduly a veškeré knihovny třetích stran.

Výčet modulů, které se načítají Bootstrap a Platform classloaderem je možné dohledat v dotazu ze Stack Overflow.

Hierarchie těchto tří classloaderů je stejná, jako v našem výčtu. Platform má jako svého rodiče nastavený Bootstrap a sám je rodičem System (Application). Bootstrap žádného rodiče nemá.

Načítání tříd má jasně definované flow, které můžeme vidět na obrázku níže:

Proces načítání tříd - Moduly v Javě

Pojďme si jednotlivé kroky blíže popsat:

  1. Na začátku je požadavek pro načtení třídy.
  2. Zkontroluje se, zdali se už třída někdy v minulosti nenačítala, pokud ano, vrátí se z cache (tento bod není v grafu).
  3. Nejprve se požadavek dostane k System (Application) classloaderu, který ho rovnou deleguje na svého rodiče - Platform classloader.
  4. Platform classloader opět rovnou deleguje požadavek na svého rodiče - Bootstrap classloader.
  5. Bootstrap classloader žádného rodiče nemá, takže se podívá, jestli je schopný požadovanou třídu načíst. Pokud ano, třídu vrátí. Pokud ne, vrátí se vyhledávání třídy zpět do Platform classloaderu.
  6. Platform classloader se nyní už podívá, jestli je schopný třídu načíst. Pokud ano, třídu vrátí. Pokud ne, vrátí vyhledávání třídy zpět do System (Application) classloaderu.
  7. System (Application) classloader je poslední, kdo může třídu načíst. V případě nezdaru se vyhodí velmi známá a nepříjemná výjimka ClassNotFoundException.

V ukázce níže uvidíte po spuštění kódu, že různé třídy opravdu vrací různé classloadery:

System.out.println(System.Logger.class.getClassLoader());
System.out.println(java.sql.SQLPermission.class.getClassLoader());
System.out.println(Program.class.getClassLoader());

Výstup:

Konzolová aplikace
null
jdk.internal.loader.ClassLoaders$PlatformClassLoader@273f6b01
jdk.internal.loader.ClassLoaders$AppClassLoader@42110406

Classloadery hrají důležitou roli v kombinaci s moduly. Když už víme, že existují mechanismy pro načítání tříd, pojďme si ještě v rychlosti definovat pojem class shadowing.

Class shadowing

Class shadowing je nepříjemný následek způsobu načítání tříd v Javě 8 a v dřívějších verzích.

Mějme dvě knihovny, které z nějakého důvodu mají stejný název balíčku a obsahují také stejnou metodu. Taková situace typicky nastane v případě, kdy naše aplikace využívá mnoho knihoven. Tyto knihovny mají také své závislosti, ovšem jejich závislosti už můžou mít starší verzi. Takže se nakonec stane, že knihovna bude na classpath vícekrát.

V takovém případě je použita třída z knihovny, která je na classpath nalezena jako první. Moduly by měly tomuto chování zabránit, protože musí mít unikátní názvy.

Dokud budou používány knihovny, které nejsou modularizovány, bude vždy existovat riziko shadowingu.

Typy modulů

Moduly v Javě lze rozdělit do čtyř kategorií: systémové, aplikační, automatické a bezejmenné.

Systémové moduly

Mezi systémové moduly patří veškeré moduly z Java SE a zároveň moduly z JDK. Tyto moduly lze používat bez žádného speciálního nastavování a jsou automaticky dostupné.

Aplikační moduly

Všechny ostatní moduly, které my jako programátoři vytvoříme, se řadí mezi aplikační moduly. Každý námi vytvořený modul musí mít v aplikaci, kterou vyvíjíme, unikátní název. Pokud to nezajistíme, aplikaci nepůjde zkompilovat.

Automatické moduly

Knihovny, které ještě nebyly modularizovány, můžou být zařazeny mezi automatické moduly, pokud se vloží na modulepath. Automatické moduly dostanou název automaticky. Buď se použije název definovaný v manifestu pod hlavičkou Automatic-Module-Name, nebo se název automaticky odvodí z názvu JAR souboru, ve kterém se automatický modul nachází.

Automatické moduly automaticky exportují všechny balíčky a povolují přístup přes reflexi. Dále jim je umožněno číst veřejné API ostatních modulů (včetně pojmenovaných).

Bezejmenné moduly

Knihovny, které ještě nebyly modularizovány, budou zařazeny mezi bezejmenné moduly, pokud se vloží na classpath.

Bezejmenné moduly také automaticky exportují všechny balíčky a povolují přístup přes reflexi. Dále můžou číst veřejné API ostatních modulů dostupných na modulepath a všechny knihovny dostupné na classpath.

Na druhou stranu není možné používat bezejmenné moduly v pojmenovaných modulech.

Definice modulu

Moduly se definují ve speciálním souboru nazvaném module-info.java. Tento soubor najdeme v src složce daného modulu. V případě Mavenu se definice modulu vkládá do složky src/main/java.

Jako první je třeba v souboru uvést klíčové slovo module následované názvem modulu. Modul si můžeme nazvat, jak potřebujeme. V případě Javy zvolili vývojáři jednoduché pojmenování java.nazev_mo­dulu. Tedy java.base, java.logging, java.xml atd. Knihovna JavaFX používá podobné pojmenování: javafx.base, javafx.controls, javafx.fxml. Velké množství knihoven ale používá jako název modulu název balíčků, ve kterých se knihovna nachází. Například JAXB knihovna má název modulu jakarta.xml.bind.

Po názvu modulu následují složené závorky. Prázdný modul, který na ničem nezávisí a nic neexportuje, může vypadat například takto:

module cz.itnetwork.moduly.prvnimodul {
  // zde se budou definovat veřejně dostupné moduly
  // a závislosti
}

Dále je vhodné dodržet jmenné konvence projektu, modulů a výsledných artefaktů. Mějme projekt nazvaný cz.itnetwork.moduly, který je rozdělen na několik samostatných modulů prvnimodul a druhymodul. Takový projekt může mít například následující strukturu:

cz.itnetwork.moduly - kořenová složka celého projektu
|- cz.itnetwork.moduly.prvnimodul - první modul
|  |-src
|    |- module-info.java - module info prvního modulu
|    |- cz - balíčky prvního modulu
|        |- itnetwork
|          |- moduly
|             |- prvnimodul
|                |- TridaA.java - třída v prvním modulu
|- cz.itnetwork.moduly.druhymodul - druhý modul
   |-src
     |- module-info.java - module info druhého modulu
     |- cz - balíčky druhého modulu
        |- itnetwork
           |- moduly
              |- druhymodul
                 |- TridaB.java - třída ve druhém modulu

Artefakty můžeme pojmenovat jen po modulu prvnimodul, případně mohou obsahovat i celý název cz.itnetwork.moduly.prvnimodul. Vše si ukážeme na praktických příkladech, které nás čekají v dalších lekcích.

V další lekci, Vytvoření prvního modulu v Javě, si po vzoru prvních lekcí OOP vytvoříme projekt "Zdravic" se třemi moduly a ukážeme si, jak jednotlivé moduly konfigurovat, aby byly schopné mezi sebou komunikovat.


 

Předchozí článek
Úvod do modulů v Javě
Všechny články v sekci
Moduly v Javě
Přeskočit článek
(nedoporučujeme)
Vytvoření prvního modulu v Javě
Článek pro vás napsal Petr Štechmüller
Avatar
Uživatelské hodnocení:
6 hlasů
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity