2. diel - Classloader a prvý modul v Jave
V predchádzajúcej lekcii, Úvod do modulov v Jave , sme sa zoznámili so štruktúrou a vlastnosťami modulov a ukázali si, ako pomáhajú riešiť problematiku závislosti balíčkov.
V tejto lekcii budeme ešte chvíľku pokračovať v teórii. V dnešnom Java tutoriále si povieme, ako funguje classloader a zoznámime sa s rôznymi typmi modulov. Na záver si ukážeme, ako taký modul vyzerá.
Načítanie tried
O načítanie tried v aplikácii sa stará tzv. classloader. V Jave existujú celkom tri druhy classloaderov:
- Bootstrap classloader - základný classloader,
naimplementovaný priamo v C++ (nie v Jave), starajúci sa o načítanie
kritických modulov, ako je
java.base
, - Platform classloader - tento classloader je zodpovedný za
načítanie všetkých nekritických modulov, ktoré ale sú stále súčasťou
JRE, ako je
java.sql
, - System (Application) classloader - posledný classloader načíta všetky ostatné moduly, typicky sa jedná o nami vytvorené moduly a všetky knižnice tretích strán.
Hierarchia týchto troch classloaderov je rovnaká, ako v našom zozname. Platform má ako svojho rodiča nastavený Bootstrap a sám je rodičom System (Application). Bootstrap žiadneho rodiča nemá.
Načítanie tried má jasne definované flow, ktoré môžeme vidieť na obrázku nižšie:
Poďme si jednotlivé kroky bližšie popísať:
- Na začiatku je požiadavka na načítanie triedy.
- Skontroluje sa, či sa už trieda niekedy v minulosti nenačítala, pokiaľ áno, vráti sa z cache (tento bod nie je v grafe).
- Najprv sa požiadavka dostane k System (Application) classloaderu, ktorý ho rovno deleguje na svojho rodiča - Platform classloader.
- Platform classloader opäť rovno deleguje požiadavku na svojho rodiča - Bootstrap classloader.
- Bootstrap classloader žiadneho rodiča nemá, takže sa pozrie, či je schopný požadovanú triedu načítať. Ak áno, triedu vráti. Ak nie, vráti sa vyhľadávanie triedy späť do Platform classloaderu.
- Platform classloader sa teraz už pozrie, či je schopný triedu načítať. Ak áno, triedu vráti. Ak nie, vráti vyhľadávanie triedy späť do System (Application) classloaderu.
- System (Application) classloader je posledný, kto môže
triedu načítať. V prípade neúspechu sa vyhodí veľmi známa a nepríjemná
výnimka
ClassNotFoundException
.
{JAVA_CONSOLE}
System.out.println(System.Logger.class.getClassLoader());
System.out.println(java.sql.SQLPermission.class.getClassLoader());
System.out.println(Program.class.getClassLoader());
{/JAVA_CONSOLE}
Výstup:
Konzolová aplikácia
null
jdk.internal.loader.ClassLoaders$PlatformClassLoader@273f6b01
jdk.internal.loader.ClassLoaders$AppClassLoader@42110406
Classloadery hrajú dôležitú úlohu v kombinácii s modulmi. Keď už vieme, že existujú mechanizmy na načítanie tried, poďme si ešte v rýchlosti definovať pojem class shadowing.
Class shadowing
Class shadowing je nepríjemný následok spôsobu načítania tried v Jave 8 av predchádzajúcich verziách.
Majme dve knižnice, ktoré z nejakého dôvodu majú rovnaký názov balíčka a obsahujú aj rovnakú metódu. Takáto situácia typicky nastane v prípade, keď naša aplikácia využíva mnoho knižníc. Tieto knižnice majú tiež svoje závislosti, avšak ich závislosti už môžu mať staršiu verziu. Takže sa nakoniec stane, že knižnica bude na classpath viackrát.
V takom prípade je použitá trieda z knižnice, ktorá je na classpath nájdená ako prvá. Moduly by mali tomuto správaniu zabrániť, pretože musia mať unikátne názvy.
Kým budú používané knižnice, ktoré nie sú modularizované, bude vždy existovať riziko shadowingu.
Typy modulov
Moduly v Jave možno rozdeliť do štyroch kategórií: systémové, aplikačné, automatické a bezmenné.
Systémové moduly
Medzi systémové moduly patria všetky moduly z Java SE a zároveň moduly z JDK. Tieto moduly je možné používať bez žiadneho špeciálneho nastavovania a sú automaticky dostupné.
Aplikačné moduly
Všetky ostatné moduly, ktoré my ako programátori vytvoríme, sa radia medzi aplikačné moduly. Každý nami vytvorený modul musí mať v aplikácii, ktorú vyvíjame unikátny názov. Pokiaľ to nezaistíme, aplikáciu nepôjde skompilovať.
Automatické moduly
Knižnice, ktoré ešte neboli modularizované, môžu byť zaradené medzi
automatické moduly, ak sa vložia na modulepath. Automatické moduly
dostanú názov automaticky. Buď sa použije názov definovaný v manifeste pod
hlavičkou Automatic-Module-Name
, alebo sa názov
automaticky odvodí z názvu JAR súboru, v ktorom sa
automatický modul nachádza.
Automatické moduly automaticky exportujú všetky balíčky a povoľujú prístup cez reflexiu. Ďalej im je umožnené čítať verejné API ostatných modulov (vrátane pomenovaných).
Bezmenné moduly
Knižnice, ktoré ešte neboli modularizované, budú zaradené medzi bezmenné moduly, ak sa vložia na classpath.
Bezmenné moduly tiež automaticky exportujú všetky balíčky a povoľujú prístup cez reflexiu. Ďalej môžu čítať verejné API ostatných modulov dostupných na modulepath a všetky knižnice dostupné na classpath.
Na druhej strane nie je možné používať bezmenné moduly v pomenovaných moduloch.
Definícia modulu
Moduly sa definujú v špeciálnom súbore nazvanom
module-info.java
. Tento súbor nájdeme v src
zložke
daného modulu. V prípade Mavenu sa definícia modulu vkladá
do zložky src/main/java
.
Ako prvé je potrebné v súbore uviesť kľúčové slovo
module
nasledované názvom modulu. Modul si môžeme nazvať, ako
potrebujeme. V prípade Javy, zvolili vývojári jednoduché pomenovanie
java.nazov_modulu . Teda java.base
,
java.logging
, java.xml
atď. Knižnica JavaFX
používa podobné pomenovanie: javafx.base
,
javafx.controls
, javafx.fxml
. Veľké množstvo
knižníc ale používa ako názov modulu názov balíčkov, v ktorých sa
knižnica nachádza. Napríklad JAXB knižnica má názov modulu
jakarta.xml.bind
.
Po názve modulu nasledujú zložené zátvorky. Prázdny modul, ktorý na ničom nezávisí a nič neexportuje, môže vyzerať napríklad takto:
module cz.itnetwork.moduly.prvnimodul { // zde se budou definovat veřejně dostupné moduly // a závislosti }
Ďalej je vhodné dodržať menné konvencie projektu, modulov a výsledných
artefaktov. Majme projekt nazvaný cz.itnetwork.moduly
, ktorý je
rozdelený na niekoľko samostatných modulov prvnimodul
a
druhymodul
. Takýto projekt môže mať napríklad nasledujúcu
štruktúru:
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 pomenovať len po module prvnimodul
,
prípadne môžu obsahovať aj celý názov
cz.itnetwork.moduly.prvnimodul
. Všetko si ukážeme na
praktických príkladoch, ktoré nás čakajú v ďalších lekciách.
V ďalšej lekcii, Vytvorenie prvého modulu v Jave , si po vzore prvých lekcií OOP vytvoríme projekt "Zdravíc" s tromi modulmi a ukážeme si, ako jednotlivé moduly konfigurovať, aby boli schopné medzi sebou komunikovať.