Programiranje

Osnove nalagalcev razreda Java

Koncept nalagalca razredov, enega izmed temeljev navideznega stroja Java, opisuje vedenje pretvorbe imenovanega razreda v bite, ki so odgovorni za izvajanje tega razreda. Ker obstajajo nalagalniki razredov, čas izvajanja Jave pri izvajanju programov Java ne mora vedeti ničesar o datotekah in datotečnih sistemih.

Kaj delajo nakladalci razreda

Razredi se uvedejo v okolje Java, ko se nanje poimenujejo v razredu, ki se že izvaja. Nekaj ​​čarovnije se nadaljuje, da se zažene prvi razred (zato morate prijaviti glavni () metoda kot statična, pri čemer kot argument vzame niz matrike), toda ko se ta razred zažene, bodoči poskusi nalaganja razredov izvaja nalagalnik razredov.

Najenostavneje je, da nalagalnik razredov ustvari raven prostor imen teles razredov, na katera se sklicuje ime niza. Opredelitev metode je:

Razred r = loadClass (niz className, logična razrešitev); 

Spremenljivka className vsebuje niz, ki ga nalagalnik razredov razume in se uporablja za enolično identifikacijo izvedbe razreda. Spremenljivka razreši je zastavica, ki sporoča nalagalcu razredov, da je treba razrešiti razrede, na katere se sklicuje to ime razreda (to pomeni, da je treba naložiti tudi vse referenčne razrede).

Vsi navidezni stroji Java vključujejo nalagalnik enega razreda, ki je vdelan v navidezni stroj. Ta vdelani nalagalnik se imenuje nakladač prvotnega razreda. Je nekoliko poseben, ker navidezni stroj domneva, da ima dostop do repozitorija zaupanja vredni razredi ki jih lahko VM zažene brez preverjanja.

Prvotni nalagalnik razreda izvaja privzeto izvedbo loadClass (). Tako ta koda razume ime razreda java.lang.Object je shranjena v datoteki s predpono java / lang / Object.class nekje na poti razreda. Ta koda izvaja tudi iskanje poti do razreda in pregledovanje datotek zip za razrede. Resnično kul pri zasnovi tega je, da lahko Java spremeni svoj model shranjevanja razredov, tako da spremeni nabor funkcij, ki izvajajo nalagalnik razredov.

Kopate se po drobovju navideznega stroja Java, boste odkrili, da je prvotni nalagalnik razredov implementiran predvsem v funkcije FindClassFromClass in ResolveClass.

Kdaj so nato naloženi razredi? Obstajata natanko dva primera: ko se izvede nova bajtoda (na primer FooClassf = novo FooClass ();) in ko se bajtkode statično sklicujejo na razred (na primer Sistem.ven).

Nakladalnik neprimarnega razreda

"Pa kaj?" boste morda vprašali.

Navidezni stroj Java ima kljuke, ki omogočajo uporabo uporabniško določenega nalagalnika razredov namesto prvotnega. Poleg tega, ker nalagalnik uporabniškega razreda dobi prvo razpoko v imenu razreda, lahko uporabnik implementira poljubno število zanimivih skladišč razredov, nenazadnje tudi strežnike HTTP - ki so Java najprej spravili s tal.

Vendar pa stanejo stroški, ker je razredni nakladalnik tako zmogljiv (na primer lahko ga zamenja) java.lang.Object s svojo različico), razredi Java, kot so apleti, ne smejo ustvariti lastnih nalagalnikov. (Mimogrede to uveljavlja nalagalnik razredov.) Ta stolpec ne bo koristen, če poskušate to narediti z programčkom, samo z aplikacijo, ki se izvaja iz zaupanja vrednega skladišča razredov (na primer lokalne datoteke).

Nalagalnik uporabniškega razreda dobi priložnost, da naloži razred, preden ga naloži prvotni razred. Zaradi tega lahko naloži podatke o izvedbi razreda iz nekega nadomestnega vira, kar je način, kako AppletClassLoader lahko naloži razrede s protokolom HTTP.

Izdelava SimpleClassLoader

Nalagalnik razredov se začne tako, da je podrazred java.lang.ClassLoader. Edina abstraktna metoda, ki jo je treba uporabiti, je loadClass (). Pretok loadClass () kot sledi:

  • Preverite ime predavanja.
  • Preverite, ali je zahtevani razred že naložen.
  • Preverite, ali je razred "sistemski" razred.
  • Poskus pridobiti razred iz skladišča tega nalagalnika razredov.
  • Določite razred za VM.
  • Rešite razred.
  • Vrnite razred kličočemu.

SimpleClassLoader se prikaže na naslednji način, z opisi tega, kar počne, vmešanih v kodo.

 javni sinhronizirani razred loadClass (String className, logična razrešitev) vrže ClassNotFoundException {Rezultat razreda; bajt classData []; System.out.println (">>>>>> Naloži razred:" + className); / * Preverite naš lokalni predpomnilnik razredov * / result = (Class) classes.get (className); if (result! = null) {System.out.println (">>>>>> vrne predpomnjeni rezultat."); vrniti rezultat; } 

Zgornja koda je prvi odsek loadClass metoda. Kot lahko vidite, potrebuje ime razreda in poišče lokalno hash tabelo, ki jo naš učitelj nalaganja ohranja v razredih, ki jih je že vrnil. Pomembno je, da to hash tabelo obdržite že od vas mora vrnite isti sklic predmeta predmeta za isto ime razreda vsakič, ko vas to zahteva. V nasprotnem primeru bo sistem verjel, da obstajata dva različna razreda z istim imenom, in vrgel a ClassCastException vsakič, ko jim dodelite referenco predmeta. Pomembno je tudi, da obdržite predpomnilnik, ker loadClass () metoda se pokliče rekurzivno, ko je razred razrešen, in boste morali vrniti predpomnjeni rezultat, namesto da bi ga lovili za drugo kopijo.

/ * Preverite pri prvotnem nalagalniku razreda * / poskusite {result = super.findSystemClass (className); System.out.println (">>>>>> vrne sistemski razred (v CLASSPATH)."); vrniti rezultat; } catch (ClassNotFoundException e) {System.out.println (">>>>>> Ni sistemski razred."); } 

Kot lahko vidite v zgornji kodi, je naslednji korak preveriti, ali lahko prvotni nalagalnik razredov razreši to ime razreda. To preverjanje je bistvenega pomena za varnost in varnost sistema. Na primer, če vrnete svoj primerek java.lang.Object kličočemu, potem ta objekt ne bo delil nobenega skupnega superrazreda z nobenim drugim predmetom! Varnost sistema je lahko ogrožena, če je vaš nalagalnik razredov vrnil lastno vrednost java.lang.SecurityManager, ki ni imel enakih pregledov kot pravi.

 / * Poskusite ga naložiti iz našega skladišča * / classData = getClassImplFromDataBase (className); if (classData == null) {vrzi nov ClassNotFoundException (); } 

Po začetnih preveritvah pridemo do zgornje kode, kjer nalagalnik preprostih razredov dobi priložnost naložiti izvedbo tega razreda. The SimpleClassLoader ima metodo getClassImplFromDataBase () ki v našem preprostem primeru zgolj predpiše imenik "store \" pred imenom razreda in doda pripono ".impl". V primeru sem izbral to tehniko, da ne bi bilo govora o tem, da je prvotni nakladalnik razreda našel naš razred. Upoštevajte, da sun.applet.AppletClassLoader predpono URL-ja baze kode s strani HTML, kjer programček živi, ​​nato nato HTTP dobi zahtevo za pridobitev bajt kod.

 / * Določite ga (razčlenite datoteko razreda) * / rezultat = defineClass (classData, 0, classData.length); 

Če je bila izvedba razreda naložena, je predzadnji korak klic defineClass () metoda iz java.lang.ClassLoader, kar lahko štejemo za prvi korak preverjanja razreda. Ta metoda je implementirana v navideznem računalniku Java in je odgovorna za preverjanje, ali so bajti razreda zakonita datoteka razreda Java. Interno je defineClass metoda izpolni podatkovno strukturo, ki jo JVM uporablja za zadrževanje razredov. Če so podatki razreda nepravilni, bo ta klic povzročil a ClassFormatError biti vržen.

 if (resolIt) {resolClass (rezultat); } 

Zadnja zahteva, specifična za nakladalnik razreda, je klic resolClass () če je logični parameter razreši je bilo res. Ta metoda naredi dve stvari: najprej povzroči, da se naložijo vsi razredi, na katere se ta razred izrecno sklicuje, in ustvari prototipni objekt za ta razred; nato pa preveritelja pozove, da opravi dinamično preverjanje legitimnosti bajt kod v tem razredu. Če preverjanje ne uspe, bo ta klic metode vrgel a Napaka povezave, najpogostejši med njimi je a VerifyError.

Upoštevajte, da za vsak razred, ki ga boste naložili, razreši spremenljivka bo vedno res. Šele ko sistem rekurzivno kliče loadClass () da lahko to spremenljivko nastavi kot false, ker ve, da je razred, ki ga prosi, že razrešen.

 classes.put (className, rezultat); System.out.println (">>>>>> Vrnitev novo naloženega razreda."); vrniti rezultat; } 

Zadnji korak v postopku je shraniti razred, ki smo ga naložili in razrešili v našo razpršilno tabelo, da ga lahko po potrebi ponovno vrnemo, nato pa vrniti Razred sklic na klicatelja.

Seveda, če bi bilo tako preprosto, ne bi bilo več o čem govoriti. Pravzaprav obstajata dve težavi, s katerimi se bodo morali spoprijeti graditelji nalagalnikov razredov, varnost in pogovor z razredi, ki jih naloži nalagalnik razredov po meri.

Varnostni razlogi

Kadarkoli imate aplikacijo, ki prek nalagalnika razredov v sistem nalaga poljubne razrede, je integriteta vaše aplikacije ogrožena. To je posledica moči razrednega nakladalca. Vzemimo si trenutek in si oglejmo enega od načinov, kako bi potencialni zlobnik lahko vdrl v vašo prijavo, če niste previdni.

Če v našem preprostem nalagalniku razredov, če prvotni nalagalnik razredov ni mogel najti razreda, smo ga naložili iz našega zasebnega skladišča. Kaj se zgodi, ko skladišče vsebuje razred java.lang.FooBar ? Nobenega razreda ni java.lang.FooBar, vendar bi ga lahko namestili tako, da bi ga naložili iz skladišča razredov. Ta razred, ker bi imel dostop do katere koli paketno zaščitene spremenljivke v java.lang paket, lahko manipulira z nekaterimi občutljivimi spremenljivkami, tako da lahko kasnejši razredi spodkopavajo varnostne ukrepe. Zato je eno od nalog katerega koli nakladača razreda to zaščitite sistemski imeniški prostor.

V naš preprost nalagalnik razredov lahko dodamo kodo:

 če (className.startsWith ("java.")) vrže newClassNotFoundException (); 

takoj po klicu findSystemClass nad. S to tehniko lahko zaščitite kateri koli paket, za katerega ste prepričani, da naložena koda nikoli ne bo imela razloga za nalaganje novega razreda v neki paket.

Drugo tveganje je, da mora biti posredovano ime preverjeno veljavno ime. Razmislite o sovražni aplikaciji, ki je kot ime razreda uporabila ime razreda ".. \ .. \ .. \ .. \ netscape \ temp \ xxx.class". Jasno je, da če bi nalagalnik razredov to ime preprosto predstavil našemu poenostavljenemu nalagalniku datotečnega sistema, bi to lahko naložilo razred, ki ga naša aplikacija dejansko ni pričakovala. Pred iskanjem po lastnem skladišču razredov je torej dobro napisati metodo, ki preverja celovitost imen predavanj. Nato pokličite to metodo, preden začnete iskati po svojem skladišču.

Uporaba vmesnika za premostitev vrzeli

Druga neintuitivna težava pri delu z nalagalniki razredov je nezmožnost predvajanja predmeta, ki je bil ustvarjen iz naloženega razreda v prvotni razred. Vrnjeni objekt morate oddati, ker je tipična uporaba nalagalnika razredov po meri nekako takšna:

 CustomClassLoader ccl = novo CustomClassLoader (); Predmet o; Razred c; c = ccl.loadClass ("someNewClass"); o = c.newInstance (); ((SomeNewClass) o) .someClassMethod (); 

Vendar ne morete igrati o do SomeNewClass ker samo nalagalnik razredov po meri "ve" za novi razred, ki ga je pravkar naložil.

Za to sta dva razloga. Prvič, razredi v navideznem računalniku Java veljajo za uničljive, če imajo vsaj en skupen kazalec razredov. Vendar pa bodo razredi, ki jih naložita dva različna nakladalnika razredov, imeli dva kazalca različnih razredov in nobenega skupnega razreda (razen java.lang.Object običajno). Drugič, ideja o nalaganju razredov po meri je nalaganje razredov po aplikacija je razmeščena, zato aplikacija vnaprej ne ve za razrede, ki jih bo naložila. Ta dilema se reši tako, da se aplikaciji in naloženemu razredu da skupni razred.

Obstajata dva načina za ustvarjanje tega skupnega razreda: ali mora biti naložen razred podrazred razreda, ki ga je aplikacija naložila iz svojega zaupanja vrednega repozitorija, ali pa mora naložiti razred implementirati vmesnik, ki je bil naložen iz zaupanja vrednega repozitorija. Tako imata naložen razred in razred, ki ne deli celotnega prostora imen nalagalnika razredov po meri, skupen razred. V primeru uporabljam vmesnik z imenom LocalModule, čeprav bi to lahko prav tako enostavno naredili za razred in ga uvrstili v podrazred.

Najboljši primer prve tehnike je spletni brskalnik. Razred, ki ga definira Java in ga izvajajo vsi apleti, je java.applet.Applet. Ko razred naloži AppletClassLoader, ustvarjeni primerek predmeta se odda v primerek Applet. Če ta igralska zasedba uspe v() metoda se imenuje. V svojem primeru uporabljam drugo tehniko, vmesnik.

Poigravanje s primerom

Za zaokrožitev primera sem ustvaril še nekaj

.java

datotek. To so:

 javni vmesnik LocalModule {/ * Zaženi modul * / void start (možnost niza); } 
$config[zx-auto] not found$config[zx-overlay] not found