Nakladače tried v Jave

1. Úvod do triedových nakladačov

Nakladače triedy sú zodpovedné za načítanie tried Java počas behu dynamicky do JVM (Java Virtual Machine). Tiež sú súčasťou JRE (Java Runtime Environment). Preto JVM nemusí vedieť o základných súboroch alebo súborových systémoch, aby mohol spúšťať programy Java vďaka zavádzačom tried.

Tieto triedy Java sa tiež nenačítajú do pamäte naraz, ale ak to vyžaduje aplikácia. To je miesto, kde sa na scénu dostanú nakladače tried. Sú zodpovední za načítanie tried do pamäte.

V tomto výučbe sa budeme rozprávať o rôznych druhoch vstavaných zavádzačov tried, o tom, ako fungujú, a o úvode do našej vlastnej implementácie.

2. Typy vstavaných nakladačov triedy

Začnime tým, že sa na jednoduchom príklade naučíme, ako sa rôzne triedy načítavajú pomocou rôznych nakladačov tried:

public void printClassLoaders () hodí ClassNotFoundException {System.out.println ("Classloader tejto triedy:" + PrintClassLoader.class.getClassLoader ()); System.out.println ("Classloader protokolovania:" + Logging.class.getClassLoader ()); System.out.println ("Classloader of ArrayList:" + ArrayList.class.getClassLoader ()); }

Po vykonaní sa vytlačí vyššie uvedená metóda:

Zavádzač tried tejto triedy: [chránený e-mailom] Zavádzač tried protokolovania: [chránený e-mailom] Zavádzač tried ArrayList: null

Ako vidíme, sú tu tri rôzne nakladače triedy; aplikácia, rozšírenie a bootstrap (zobrazené ako nulový).

Načítač aplikačnej triedy načíta triedu, kde je obsiahnutá vzorová metóda. Načítač aplikácií alebo systémových tried načítava naše vlastné súbory do cesty triedy.

Ďalej rozšírenie jeden načíta Protokolovanie trieda. Načítavače tried rozšírenia načítajú triedy, ktoré sú rozšírením štandardných základných tried Java.

Nakoniec bootstrap načíta ArrayList trieda. Zavádzač bootstrap alebo primordial class je rodičom všetkých ostatných.

Vidíme však, že posledný, pre ArrayList zobrazí sa nulový vo výstupe. Je to preto, že zavádzač tried bootstrap je napísaný v natívnom kóde, nie v jazyku Java - takže sa nezobrazuje ako trieda Java. Z tohto dôvodu sa bude správanie zavádzača triedy bootstrap v rámci JVM líšiť.

Poďme si teraz predstaviť podrobnejšie informácie o každom z týchto nakladačov triedy.

2.1. Zavádzač triedy Bootstrap

Triedy Java sú načítané inštanciou java.lang.ClassLoader. Nakladače tried sú však triedy samotné. Preto je otázkou, kto načíta java.lang.ClassLoader sám?

Toto je miesto, kde na scénu prichádza bootstrap alebo prvotný zavádzač triedy.

Zvyčajne je zodpovedný hlavne za načítanie interných tried JDK rt.jar a ďalšie základné knižnice umiestnené v $ JAVA_HOME / jre / lib adresár. Navyše, Nakladač triedy Bootstrap slúži ako rodič všetkých ostatných ClassLoader inštancie.

Tento zavádzač triedy bootstrap je súčasťou základného JVM a je napísaný v natívnom kóde ako bolo zdôraznené vo vyššie uvedenom príklade. Rôzne platformy môžu mať rôzne implementácie tohto konkrétneho zavádzača triedy.

2.2. Loader triedy rozšírenia

The Loader triedy rozšírení je potomkom zavádzača tried bootstrap a stará sa o načítanie rozšírení štandardných základných tried Java aby bola k dispozícii všetkým aplikáciám bežiacim na platforme.

Nakladač triedy rozšírení sa zvyčajne načítava z adresára rozšírení JDK $ JAVA_HOME / lib / ext adresár alebo akýkoľvek iný adresár uvedený v priečinku java.ext.dirs systémový majetok.

2.3. Loader triedy systému

Na druhej strane zavaděč tried systému alebo aplikácií sa stará o načítanie všetkých tried aplikačných úrovní do JVM. Načíta súbory nájdené v premennej prostredia classpath, -classpath alebo -cp možnosť príkazového riadku. Tiež je to dieťa Classloaderu Extensions.

3. Ako fungujú triedne nakladače?

Zavádzače tried sú súčasťou Java Runtime Environment. Keď JVM požiada o triedu, načítač triedy sa pokúsi lokalizovať triedu a načítať definíciu triedy do modulu runtime pomocou plne kvalifikovaného názvu triedy.

The java.lang.ClassLoader.loadClass () metóda je zodpovedná za načítanie definície triedy do runtime. Pokúša sa načítať triedu na základe plne kvalifikovaného názvu.

Ak trieda ešte nie je načítaná, deleguje požiadavku na nakladač nadradenej triedy. Tento proces sa deje rekurzívne.

Ak nakladač nadradenej triedy nakoniec triedu nenájde, zavolá podradená trieda java.net.URLClassLoader.findClass () metóda na hľadanie tried v samotnom súborovom systéme.

Ak ani posledný podriadený zavádzač triedy nedokáže načítať triedu, hodí sa java.lang.NoClassDefFoundError alebo java.lang.ClassNotFoundException.

Pozrime sa na príklad výstupu, keď je vyvolaná ClassNotFoundException.

java.lang.ClassNotFoundException: com.baeldung.classloader.SampleClassLoader na java.net.URLClassLoader.findClass (URLClassLoader.java:381) na java.lang.ClassLoader.loadClass (ClassLoader.java:424) na java.lang.ClassLoader. loadClass (ClassLoader.java:357) na java.lang.Class.forName0 (natívna metóda) na java.lang.Class.forName (Class.java:348)

Ak prejdeme sledom udalostí hneď od volania java.lang.Class.forName (), môžeme pochopiť, že sa najskôr pokúsi načítať triedu pomocou nakladača nadradenej triedy a potom java.net.URLClassLoader.findClass () hľadať samotnú triedu.

Ak triedu stále nenájde, hodí a ClassNotFoundException.

Existujú tri dôležité vlastnosti nakladačov triedy.

3.1. Model delegovania

Nakladače tried sa riadia modelom delegovania, kde na požiadanie nájsť triedu alebo zdroj, a ClassLoader inštancia deleguje vyhľadávanie triedy alebo zdroja na nakladač nadradenej triedy.

Povedzme, že máme požiadavku na načítanie triedy aplikácie do JVM. Nakladač triedy systému najskôr deleguje načítanie tejto triedy na svoj nadradený zavádzač triedy rozšírenia, ktorý ho následne deleguje na zavádzač triedy bootstrap.

Iba v prípade, že bootstrap a potom zavádzač triedy rozšírenia nie je pri načítaní triedy úspešný, sa načítač triedy systému pokúsi načítať samotnú triedu.

3.2. Jedinečné triedy

V dôsledku modelu delegovania je ľahké zabezpečiť jedinečné triedy, pretože sa vždy snažíme delegovať smerom nahor.

Ak zavádzač nadradenej triedy nedokáže triedu nájsť, iba potom by sa to pokúsila aktuálna inštancia urobiť sama.

3.3. Viditeľnosť

Navyše, nakladače detských tried sú viditeľné pre triedy načítané nakladačmi jeho materských tried.

Napríklad triedy načítané zavádzačom systémových tried majú viditeľnosť pre triedy načítané rozšírením a zavádzačmi tried Bootstrap, ale nie naopak.

Na ilustráciu, ak je trieda A načítaná zavádzačom triedy aplikácií a trieda B načítaná rozšírením triedy, potom sú triedy A aj B viditeľné, pokiaľ ide o ostatné triedy načítané zavádzačom triedy aplikácií.

Trieda B je napriek tomu jedinou triedou viditeľnou, pokiaľ ide o ostatné triedy načítané rozšírením triedy.

4. Vlastný ClassLoader

Vstavaný zavádzač triedy by postačoval vo väčšine prípadov, keď sú súbory už v súborovom systéme.

V scenároch, keď musíme načítať triedy z lokálneho pevného disku alebo zo siete, však možno budeme musieť použiť vlastné zavádzače tried.

V tejto časti sa budeme venovať niektorým ďalším prípadom použitia nakladačov vlastných tried a ukážeme si, ako ich vytvoriť.

4.1. Prípady použitia vlastných nakladačov triedy

Načítavače vlastných tried sú užitočné nielen na načítanie triedy počas behu, môže ísť o niekoľko prípadov použitia:

  1. Pomoc pri úprave existujúceho bytového kódu, napr. tkáčski agenti
  2. Vytváranie tried dynamicky vyhovujúcich potrebám používateľa. napr. v JDBC sa prepínanie medzi rôznymi implementáciami ovládačov vykonáva prostredníctvom načítania dynamickej triedy.
  3. Implementácia mechanizmu verzovania tried pri načítaní rôznych bajtových kódov pre triedy s rovnakými názvami a balíkmi. To je možné vykonať buď pomocou nástroja na načítanie tried adries URL (načítanie pohárov prostredníctvom adries URL) alebo pomocou nástrojov na načítanie vlastných tried.

Existuje viac konkrétnych príkladov, kedy by sa vám mohli hodiť nakladače vlastných tried.

Prehliadače napríklad používajú na načítanie spustiteľného obsahu z webových stránok vlastný zavádzač tried. Prehliadač môže načítať applety z rôznych webových stránok pomocou samostatných zavádzačov tried. Prehliadač appletov, ktorý sa používa na spustenie appletov, obsahuje a ClassLoader ktorý pristupuje na webovú stránku na vzdialenom serveri namiesto toho, aby hľadal v lokálnom súborovom systéme.

A potom načíta surové súbory bytecode cez HTTP a premení ich na triedy vo vnútri JVM. Aj keď tieto applety majú rovnaký názov, považujú sa za rôzne komponenty, ak sú načítané rôznymi zavádzačmi triedy.

Teraz, keď chápeme, prečo sú načítače vlastných tried relevantné, poďme implementovať podtriedu ClassLoader rozšíriť a zhrnúť funkčnosť spôsobu, akým JVM načítava triedy.

4.2. Vytvára sa náš vlastný zavádzač tried

Pre ilustráciu si povedzme, že musíme načítať triedy zo súboru pomocou vlastného zavádzača tried.

Musíme predĺžiť ClassLoader triedy a prepísať findClass () metóda:

public class CustomClassLoader extends ClassLoader {@Override public Class findClass (String name) throws ClassNotFoundException {byte [] b = loadClassFromFile (name); návrat defineClass (name, b, 0, b.length); } private byte [] loadClassFromFile (String fileName) {InputStream inputStream = getClass (). getClassLoader (). getResourceAsStream (fileName.replace ('.', File.separatorChar) + ".class"); byte [] vyrovnávacia pamäť; ByteArrayOutputStream byteStream = nový ByteArrayOutputStream (); int nextValue = 0; try {while ((nextValue = inputStream.read ())! = -1) {byteStream.write (nextValue); }} chytit (IOException e) {e.printStackTrace (); } buffer = byteStream.toByteArray (); spätný nárazník; }}

Vo vyššie uvedenom príklade sme definovali vlastný zavádzač tried, ktorý rozširuje predvolený zavádzač tried a načíta bajtové pole zo zadaného súboru.

5. Porozumenie java.lang.ClassLoader

Poďme diskutovať o niekoľkých základných metódach z java.lang.ClassLoader triedy, aby ste mali jasnejší obraz o tom, ako to funguje.

5.1. The loadClass () Metóda

public Class loadClass (názov reťazca, logické riešenie) hodí ClassNotFoundException {

Táto metóda je zodpovedná za načítanie triedy s daným parametrom názvu. Parameter name odkazuje na plne kvalifikovaný názov triedy.

Virtuálny stroj Java sa vyvolá loadClass () metóda riešenia odkazov na triedy nastavenie riešenia na pravda. Nie vždy je však potrebné triedu vyriešiť. Ak potrebujeme iba zistiť, či trieda existuje alebo nie, potom je parameter vyriešiť nastavený na nepravdivé.

Táto metóda slúži ako vstupný bod pre zavádzač triedy.

Môžeme sa pokúsiť pochopiť vnútorné fungovanie loadClass () metóda zo zdrojového kódu java.lang.ClassLoader:

chránená trieda loadClass (názov reťazca, logické riešenie) hodí ClassNotFoundException {synchronized (getClassLoadingLock (name)) {// Najskôr skontrolujte, či už bola trieda načítaná Class c = findLoadedClass (name); if (c == null) {long t0 = System.nanoTime (); skúsiť {if (rodič! = null) {c = rodič.loadClass (meno, nepravda); } else {c = findBootstrapClassOrNull (meno); }} catch (ClassNotFoundException e) {// ClassNotFoundException vyhodené, ak trieda nenájdená // z načítača rodičovskej triedy, ktorý nemá hodnotu null} if (c == null) {// Ak sa stále nenachádza, vyvolajte findClass v poradí // na nájsť triedu. c = findClass (meno); }} if (vyriešiť) {resolveClass (c); } návrat c; }}

Predvolená implementácia metódy vyhľadáva triedy v nasledujúcom poradí:

  1. Vyvolá findLoadedClass (reťazec) metóda zistiť, či je trieda už načítaná.
  2. Vyvolá loadClass (reťazec) metóda na nakladači nadradenej triedy.
  3. Vyvolajte findClass (reťazec) metóda na vyhľadanie triedy.

5.2. The defineClass () Metóda

chránená konečná trieda defineClass (názov reťazca, bajt [] b, int vypnutý, int len) vyvolá ClassFormatError

Táto metóda je zodpovedná za prevod poľa bajtov na inštanciu triedy. A kým triedu použijeme, musíme ju vyriešiť.

V prípade, že dáta neobsahovali platnú triedu, hodí a ClassFormatError.

Túto metódu tiež nemôžeme prepísať, pretože je označená ako konečná.

5.3. The findClass () Metóda

chránená trieda findClass (názov reťazca) hodí ClassNotFoundException

Táto metóda vyhľadá triedu s plne kvalifikovaným názvom ako parametrom. Túto metódu musíme prepísať v implementáciách zavádzača vlastných tried, ktoré sa riadia delegačným modelom pre načítanie tried.

Tiež loadClass () vyvolá túto metódu, ak zavádzač nadradenej triedy nemôže nájsť požadovanú triedu.

Predvolená implementácia vyvolá a ClassNotFoundException ak žiadny rodič nakladača triedy nenájde triedu.

5.4. The getParent () Metóda

verejný konečný ClassLoader getParent ()

Táto metóda vráti načítanie nadradenej triedy na delegovanie.

Niektoré implementácie, ako napríklad implementácia uvedená v časti 2., sa používajú nulový reprezentovať zavádzač triedy bootstrap.

5.5. The getResource () Metóda

verejná URL getResource (názov reťazca)

Táto metóda sa pokúša nájsť zdroj s daným menom.

Najprv sa deleguje na nakladač nadradenej triedy pre prostriedok. Ak je rodič nulový, hľadá sa cesta nakladača tried zabudovaného do virtuálneho stroja.

Ak to zlyhá, metóda sa vyvolá findResource (reťazec) nájsť zdroj. Názov prostriedku zadaný ako vstup môže byť relatívny alebo absolútny vzhľadom na cestu triedy.

Vráti objekt URL na čítanie prostriedku, alebo má hodnotu null, ak sa zdroj nepodarilo nájsť alebo ak vyvolávač nemá dostatočné privilégiá na vrátenie prostriedku.

Je dôležité si uvedomiť, že Java načítava zdroje z cesty triedy.

Nakoniec načítanie zdrojov v Jave sa považuje za nezávislé na umiestnení pretože nezáleží na tom, kde je spustený kód, pokiaľ je prostredie nastavené na hľadanie zdrojov.

6. Kontextové zavádzače tried

Načítavače kontextových tried vo všeobecnosti poskytujú alternatívnu metódu k schéme delegovania načítania tried zavedenej v J2SE.

Ako sme sa už dozvedeli, nakladače triedy v JVM sa riadia hierarchickým modelom tak, že každý nakladač tried má jedného rodiča, s výnimkou zavádzača tried bootstrap.

Avšak niekedy, keď základné triedy JVM potrebujú dynamicky načítať triedy alebo prostriedky poskytované vývojármi aplikácií, môže sa vyskytnúť problém.

Napríklad v JNDI sú základné funkcie implementované triedami bootstrap v rt.jar. Ale tieto triedy JNDI môžu načítať poskytovateľov JNDI implementovaných nezávislými dodávateľmi (nasadených v aplikačnej triede cesty). Tento scenár vyžaduje, aby zavádzač triedy bootstrap (zavádzač materskej triedy) načítal triedu viditeľnú pre zavádzač aplikácií (zavádzač podradenej triedy).

Delegácia J2SE tu nefunguje a aby sme tento problém obišli, musíme nájsť alternatívne spôsoby načítania triedy. A to je možné dosiahnuť pomocou zavádzačov kontextu vlákien.

The java.lang.Thread trieda má metódu getContextClassLoader () ktorý vracia ContextClassLoader pre konkrétne vlákno. The ContextClassLoader poskytuje tvorca vlákna pri načítaní zdrojov a tried.

Ak táto hodnota nie je nastavená, použije sa predvolený kontext zavádzača tried nadradeného vlákna.

7. Záver

Na spustenie programu Java sú nevyhnutné zavádzače tried. Súčasťou tohto článku je dobrý úvod.

Hovorili sme o rôznych druhoch nakladačov tried - Bootstrap, Extensions a System Class Loaders. Bootstrap slúži ako rodič pre všetky z nich a je zodpovedný za načítanie interných tried JDK. Rozšírenia a systém na druhej strane načítajú triedy z adresára rozšírení Java a z triedy classpath.

Potom sme hovorili o tom, ako fungujú načítače tried, a diskutovali sme o niektorých funkciách, ako je delegovanie, viditeľnosť a jedinečnosť, po ktorých nasledovalo krátke vysvetlenie toho, ako vytvoriť vlastný. Na záver sme poskytli úvod do nakladačov tried Context.

Ukážky kódu, ako vždy, nájdete na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found