Učitavači razreda u Javi

1. Uvod u razredne utovarivače

Razredni utovarivači su odgovorni za učitavanje Java klasa tijekom izvođenja dinamički u JVM (Java virtualni stroj). Također, dio su JRE (Java Runtime Environment). Dakle, JVM ne mora znati o temeljnim datotekama ili datotečnim sustavima da bi pokrenuo Java programe zahvaljujući učitavačima klasa.

Također, ove se Java klase ne učitavaju u memoriju odjednom, već kada to zahtijeva aplikacija. Tu dolaze do izražaja učitelji utovara. Oni su odgovorni za učitavanje klasa u memoriju.

U ovom uputstvu govorit ćemo o različitim vrstama ugrađenih učitavača klasa, načinu njihovog rada i uvodu u vlastitu prilagođenu implementaciju.

2. Vrste ugrađenih klasa utovarivača

Počnimo s učenjem kako se učitavaju različite klase pomoću različitih učitavača klasa na jednostavnom primjeru:

public void printClassLoaders () baca ClassNotFoundException {System.out.println ("Učitavač razreda ove klase: + + PrintClassLoader.class.getClassLoader ()); System.out.println ("Učitavač klase zapisnika:" + Logging.class.getClassLoader ()); System.out.println ("Učitavač razreda ArrayList:" + ArrayList.class.getClassLoader ()); }

Kada se izvrši gornja metoda ispisuje:

Učitavač klase ove klase: [zaštićen e-poštom] Učitavač klase evidentiranja: [zaštićen e-poštom] Učitavač klase ArrayList: null

Kao što vidimo, ovdje postoje tri različite klase utovarivača; aplikacija, proširenje i bootstrap (prikazuje se kao null).

Učitavač klase aplikacije učitava klasu u kojoj se nalazi primjer metode. Učitavač aplikacije ili sistemske klase učitava naše vlastite datoteke u put predavanja.

Dalje, produžetak učitava Sječa drva razred. Učitavači klasa proširenja učitavaju klase koje su proširenje standardnih osnovnih Java klasa.

Napokon, bootstrap učitava ArrayList razred. Pokretački trap ili osnovni učitavač klase roditelj je svih ostalih.

Međutim, možemo vidjeti da je posljednji izlazak za ArrayList prikazuje se null u izlazu. To je zato što je učitavač klase bootstrap napisan u izvornom kodu, a ne na Javi - pa se ne prikazuje kao Java klasa. Iz tog razloga, ponašanje učitača klase bootstrap će se razlikovati među JVM-ovima.

Razmotrimo sada detaljnije o svakom od ovih klasa utovarivača.

2.1. Učitavač klase Bootstrap

Java klase učitava instanca java.lang.ClassLoader. Međutim, učitavači klasa su i sami razredi. Stoga je pitanje tko to učitava java.lang.ClassLoader sebe?

Tu dolazi do izražaja bootstrap ili osnovni učitavač klase.

Uglavnom je odgovoran za učitavanje internih klasa JDK rt.jar i ostale osnovne knjižnice smještene u $ JAVA_HOME / jre / lib direktorij. Dodatno, Učitavač klase Bootstrap služi kao roditelj svih ostalih ClassLoader instance.

Ovaj učitač klase bootstrap dio je jezgre JVM i napisan je u izvornom kodu kao što je istaknuto u gornjem primjeru. Različite platforme mogu imati različite implementacije ovog određenog učitavača klase.

2.2. Utovarivač klase produženja

The loader klase ekstenzija podređena je loadstra klase bootstrap i brine se o učitavanju ekstenzija standardnih osnovnih Java klasa tako da je dostupan svim aplikacijama pokrenutim na platformi.

Učitavač klase proširenja obično se učitava iz direktorija proširenja JDK $ JAVA_HOME / lib / lok ili bilo koji drugi imenik spomenut u java.ext.dirs svojstvo sustava.

2.3. Učitavač sistemske klase

Učitavač sustava ili klase aplikacije, s druge strane, brine se o učitavanju svih klasa razine aplikacije u JVM. Učitava datoteke pronađene u varijabli okruženja classpath, -razredna staza ili -cp opcija naredbenog retka. Također je podređeno učitavaču razreda Extensions.

3. Kako rade razredni utovarivači?

Učitavači klase dio su Java Runtime Environment. Kada JVM zatraži klasu, učitavač klase pokušava pronaći klasu i učitati definiciju klase u vrijeme izvođenja koristeći potpuno kvalificirano ime klase.

The java.lang.ClassLoader.loadClass () metoda odgovorna je za učitavanje definicije klase u vrijeme izvođenja. Pokušava učitati klasu na temelju potpuno kvalificiranog imena.

Ako klasa već nije učitana, ona delegira zahtjev roditeljskom učitavaču klase. Taj se proces događa rekurzivno.

Na kraju, ako učitavač roditeljske klase ne pronađe klasu, tada će nazvati podređena klasa java.net.URLClassLoader.findClass () metoda za traženje klasa u samom datotečnom sustavu.

Ako ni zadnji učitač klase podređenih ne može učiti klasu, baca java.lang.NoClassDefFoundError ili java.lang.ClassNotFoundException.

Pogledajmo primjer izlaza kada se baci ClassNotFoundException.

java.lang.ClassNotFoundException: com.baeldung.classloader.SampleClassLoader na java.net.URLClassLoader.findClass (URLClassLoader.java:381) na java.lang.ClassLoader.loadClass (ClassLoader.laClass. ClassLoader. loadClass (ClassLoader.java:357) na java.lang.Class.forName0 (Native Method) na java.lang.Class.forName (Class.java:348)

Ako prođemo kroz slijed događaja odmah od poziva java.lang.Class.forName (), možemo razumjeti da prvo pokušava učitati klasu putem učitača roditeljske klase, a zatim java.net.URLClassLoader.findClass () tražiti sam razred.

Kad i dalje ne pronađe razred, baca znak ClassNotFoundException.

Tri su važne značajke klasičnih utovarivača.

3.1. Model delegiranja

Učitavači razreda slijede model delegiranja gdje na zahtjev za pronalazak klase ili resursa, a ClassLoader instanca će delegirati pretraživanje klase ili resursa u učitavač nadređene klase.

Recimo da imamo zahtjev za učitavanje klase aplikacije u JVM. Učitavač sistemske klase prvo delegira učitavanje te klase u svoj roditeljski učitavač klase ekstenzije, koji je pak delegira u učitavač klase bootstrap.

Samo ako bootstrap i zatim učitavač klase ekstenzije nisu uspješni u učitavanju klase, sistemski učitavač klase pokušava učitati samu klasu.

3.2. Jedinstvena nastava

Kao posljedicu modela delegiranja to je lako osigurati jedinstvene klase jer ih uvijek pokušavamo delegirati prema gore.

Ako učitavač nadređene klase ne može pronaći klasu, samo će trenutna instanca to pokušati učiniti sama.

3.3. Vidljivost

U Dodatku, učitavači dječje klase vidljivi su klasama koje učitavaju učitavači roditeljske klase.

Na primjer, klase učitane učitavačem sistemske klase imaju vidljivost u klase učitane učitavanjem proširenja i Bootstrap klase, ali ne i obrnuto.

Da bismo to ilustrirali, ako klasu A učitava program za učitavanje klase aplikacija, a klasu B učitava učitavač klase proširenja, tada su i klase A i B vidljive što se tiče ostalih klasa učitanih programom za učitavanje klase aplikacija.

Usprkos tome, klasa B jedina je vidljiva klasa što se tiče ostalih klasa učitanih učitavanjem produžne klase.

4. Prilagođeni ClassLoader

Ugrađeni učitavač klase bio bi dovoljan u većini slučajeva kada su datoteke već u datotečnom sustavu.

Međutim, u scenarijima u kojima trebamo učitati klase s lokalnog tvrdog diska ili mreže, možda ćemo trebati koristiti prilagođene učitavače klasa.

U ovom ćemo odjeljku pokriti neke druge slučajeve uporabe za utovarivače prilagođene klase i demonstrirati ćemo kako ih stvoriti.

4.1. Slučajevi upotrebe utovarivača prilagođene klase

Učitavači prilagođenih klasa korisni su za više od pukog učitavanja klase tijekom izvođenja, nekoliko slučajeva korištenja može uključivati:

  1. Pomoć u izmjeni postojećeg bajt-koda, na pr. sredstva za tkanje
  2. Stvaranje klasa dinamički prilagođenih potrebama korisnika. npr. u JDBC-u, prebacivanje između različitih implementacija upravljačkih programa vrši se dinamičkim učitavanjem klase.
  3. Implementacija mehanizma za izradu verzija klasa tijekom učitavanja različitih bytecodea za klase s istim imenima i paketima. To se može učiniti putem utovarivača klase URL-a (učitavanje staklenki putem URL-ova) ili pomoću učitavača prilagođenih klasa.

Postoje konkretniji primjeri gdje bi utovarivači prilagođene klase mogli dobro doći.

Na primjer, preglednici koriste prilagođeni učitavač klasa za učitavanje izvršnog sadržaja s web mjesta. Preglednik može učitati aplete s različitih web stranica pomoću zasebnih učitavača klasa. Preglednik apleta koji se koristi za pokretanje apleta sadrži a ClassLoader koja pristupa web mjestu na udaljenom poslužitelju, umjesto da gleda u lokalni sustav datoteka.

A zatim učitava sirove datoteke bytecode-a putem HTTP-a i pretvara ih u klase unutar JVM-a. Čak i ako su ovi apleti imaju isto ime, smatraju se različitim komponentama ako ih utovaruju utovarivači različitih klasa.

Sad kad shvaćamo zašto su utovarivači prilagođenih klasa relevantni, implementiramo podrazred od ClassLoader proširiti i sažeti funkcionalnost načina na koji JVM učitava klase.

4.2. Izrada našeg prilagođenog učitača klase

Radi ilustracije, recimo da trebamo učitati klase iz datoteke pomoću prilagođenog učitavača klasa.

Moramo proširiti ClassLoader klase i nadjačati findClass () metoda:

javna klasa CustomClassLoader proširuje ClassLoader {@Override javna klasa findClass (naziv niza) baca ClassNotFoundException {byte [] b = loadClassFromFile (name); return defineClass (ime, b, 0, b.dužina); } privatni bajt [] loadClassFromFile (Niz datotekeName) {InputStream inputStream = getClass (). getClassLoader (). getResourceAsStream (fileName.replace ('.', File.separatorChar) + ".class"); bajtni [] međuspremnik; ByteArrayOutputStream byteStream = novi ByteArrayOutputStream (); int nextValue = 0; pokušajte {while ((nextValue = inputStream.read ())! = -1) {byteStream.write (nextValue); }} catch (IOException e) {e.printStackTrace (); } buffer = byteStream.toByteArray (); povratni međuspremnik; }}

U gornjem primjeru definirali smo prilagođeni učitavač klase koji proširuje zadani učitavač klase i učitava bajtni niz iz navedene datoteke.

5. Razumijevanje java.lang.ClassLoader

Razgovarajmo o nekoliko osnovnih metoda iz java.lang.ClassLoader razreda kako biste dobili jasniju sliku o tome kako to funkcionira.

5.1. The loadClass () Metoda

javna klasa loadClass (naziv niza, logičko rješenje) baca ClassNotFoundException {

Ova je metoda odgovorna za učitavanje klase s parametrom imena. Parametar name odnosi se na potpuno kvalificirano ime klase.

Java Virtual Machine poziva loadClass () metoda za rješavanje referenci klasa postavka razriješi za pravi. Međutim, nije uvijek potrebno razriješiti nastavu. Ako samo trebamo utvrditi postoji li klasa ili ne, tada je parametar razvrstavanja postavljen na lažno.

Ova metoda služi kao ulazna točka za učitavač klase.

Možemo pokušati razumjeti unutarnji rad sustava loadClass () metoda iz izvornog koda java.lang.ClassLoader:

zaštićena klasa loadClass (ime niza, logičko rješavanje) baca ClassNotFoundException {sinkronizirano (getClassLoadingLock (ime)) {// Prvo, provjerite je li klasa već učitana Class c = findLoadedClass (ime); if (c == null) {long t0 = System.nanoTime (); pokušajte {if (nadređeni! = null) {c = nadređeni.loadClass (ime, netačno); } else {c = findBootstrapClassOrNull (ime); }} catch (ClassNotFoundException e) {// ClassNotFoundException bačen ako klasa nije pronađena // iz ne-null učitača nadređene klase} if (c == null) {// Ako i dalje nije pronađena, pozovite findClass kako biste // na pronađi razred. c = findClass (ime); }} ako (razriješiti) {razriješitiklasu (c); } povratak c; }}

Zadana implementacija metode traži klase sljedećim redoslijedom:

  1. Priziva findLoadedClass (niz) metoda da se vidi je li klasa već učitana.
  2. Priziva loadClass (niz) metoda na učitatelju roditeljske klase.
  3. Prizovi findClass (niz) metoda za pronalaženje razreda.

5.2. The defineClass () Metoda

zaštićena konačna klasa defineClass (naziv niza, bajt [] b, int isključen, int len) baca ClassFormatError

Ova je metoda odgovorna za pretvorbu niza bajtova u instancu klase. I prije nego što upotrijebimo nastavu, moramo je riješiti.

U slučaju da podaci ne sadrže valjanu klasu, baca se ClassFormatError.

Također, ne možemo poništiti ovu metodu jer je označena kao konačna.

5.3. The findClass () Metoda

zaštićena klasa findClass (naziv niza) baca ClassNotFoundException

Ova metoda kao parametar pronalazi klasu s potpuno kvalificiranim imenom. Moramo nadjačati ovu metodu u implementacijama prilagođenih učitavača klasa koje slijede model delegiranja za učitavanje klasa.

Također, loadClass () poziva ovu metodu ako učitavač nadređene klase nije mogao pronaći traženu klasu.

Zadana implementacija baca a ClassNotFoundException ako nijedan roditelj učitavača klase ne pronađe klasu.

5.4. The getParent () Metoda

javni konačni ClassLoader getParent ()

Ova metoda vraća učitavač nadređene klase za delegiranje.

Neke implementacije poput one viđene prije u odjeljku 2. koriste null za predstavljanje učitača klase bootstrap.

5.5. The getResource () Metoda

javni URL getResource (naziv niza)

Ova metoda pokušava pronaći resurs s danim imenom.

Prvo će delegirati učitavač nadređene klase za resurs. Ako je roditelj null, pretražuje se put učitavača klase ugrađenog u virtualni stroj.

Ako to ne uspije, tada će se metoda pozvati findResource (niz) pronaći resurs. Naziv resursa naveden kao ulaz može biti relativan ili apsolutni u odnosu na put klase.

Vraća objekt URL-a za čitanje resursa ili nulu ako resurs nije moguće pronaći ili ako pozivatelj nema odgovarajuće privilegije za vraćanje resursa.

Važno je napomenuti da Java učitava resurse s putova predavanja.

Konačno, učitavanje resursa u Javi smatra se neovisnim o lokaciji jer nije važno gdje se kôd izvodi sve dok je okruženje postavljeno za pronalaženje resursa.

6. Kontekstni učitavači razreda

Općenito, učitavači kontekstualne klase pružaju alternativnu metodu shemi delegiranja učitavanja klasa uvedenoj u J2SE.

Kao što smo već naučili, učitavači razreda u JVM-u slijede hijerarhijski model takav da svaki učitavač klasa ima jednog roditelja, osim učitača klase bootstrap.

Međutim, ponekad kada JVM osnovne klase trebaju dinamički učitavati klase ili resurse koje pružaju programeri aplikacija, mogli bismo naići na problem.

Na primjer, u JNDI se osnovna funkcionalnost implementira pomoću bootstrap klasa u rt.jar. Ali ove JNDI klase mogu učitati JNDI davatelje koje implementiraju neovisni dobavljači (raspoređeni u putovima klasa aplikacije). Ovaj scenarij poziva učitač klase bootstrap (učitavač roditeljske klase) da učita klasu vidljivu učitavaču aplikacija (učitavač podređene klase).

Delegacija J2SE ovdje ne radi i da bismo zaobišli taj problem, moramo pronaći alternativne načine učitavanja klase. A to se može postići pomoću učitavača konteksta niti.

The java.lang.Nit razred ima metodu getContextClassLoader () koji vraća ContextClassLoader za određenu nit. The ContextClassLoader pruža kreator niti pri učitavanju resursa i klasa.

Ako vrijednost nije postavljena, tada se zadani kontekst učitavača klase nadređene niti.

7. Zaključak

Učitavači klase su neophodni za izvršavanje Java programa. Dali smo dobar uvod kao dio ovog članka.

Razgovarali smo o različitim vrstama učitavača klase, naime - o učitačima Bootstrap, Extensions i System. Bootstrap služi kao roditelj za sve njih i odgovoran je za učitavanje internih klasa JDK. Proširenja i sustav, s druge strane, učitavaju klase iz direktorija Java ekstenzija odnosno staze klasa.

Zatim smo razgovarali o tome kako rade učitelji klasa i razgovarali smo o nekim značajkama kao što su delegiranje, vidljivost i jedinstvenost praćeni kratkim objašnjenjem kako stvoriti prilagođeni. Konačno, pružili smo uvod u utovarivače klase Context.

Uzorke koda, kao i uvijek, možete pronaći na GitHubu.