Klasselastere i Java

1. Introduksjon til klasselastere

Klasselastere er ansvarlige for laster Java-klasser under kjøretid dynamisk til JVM (Java Virtual Machine). De er også en del av JRE (Java Runtime Environment). Derfor trenger ikke JVM å vite om de underliggende filene eller filsystemene for å kjøre Java-programmer takket være klasselaster.

Disse Java-klassene lastes ikke inn i minnet samtidig, men når det kreves av et program. Det er her klasselastere kommer inn i bildet. De er ansvarlige for å laste klasser i minnet.

I denne veiledningen skal vi snakke om forskjellige typer innebygde klasselastere, hvordan de fungerer og en introduksjon til vår egen tilpassede implementering.

2. Typer innebygde lastere

La oss begynne med å lære hvordan forskjellige klasser lastes ved hjelp av forskjellige klasselastere ved hjelp av et enkelt eksempel:

public void printClassLoaders () kaster ClassNotFoundException {System.out.println ("Classloader i denne klassen:" + PrintClassLoader.class.getClassLoader ()); System.out.println ("Classloader of Logging:" + Logging.class.getClassLoader ()); System.out.println ("Classloader of ArrayList:" + ArrayList.class.getClassLoader ()); }

Når den ovennevnte metoden skrives ut:

Klasselaster av denne klassen: [email protected] Class loader of Logging: [email protected] Class loader of ArrayList: null

Som vi kan se, er det tre forskjellige klasselastere her; applikasjon, utvidelse og bootstrap (vises som null).

Programklasselaster laster inn klassen der eksemplet metoden finnes. En applikasjon eller systemklasselaster laster våre egne filer i klassestien.

Deretter laster utvidelsen en Hogst klasse. Extension class loaders laster klasser som er en utvidelse av standard Java-klasser.

Endelig laster bootstrap en ArrayList klasse. En bootstrap eller primordial class loader er foreldre til alle de andre.

Imidlertid kan vi se at den siste ut, for ArrayList det vises null i utgangen. Dette skyldes at bootstrap-klasselaster er skrevet i innfødt kode, ikke Java - så den vises ikke som en Java-klasse. Av denne grunn vil oppførselen til bootstrap class loader variere fra JVM.

La oss nå diskutere mer detaljert om hver av disse klasselasterne.

2.1. Bootstrap Class Loader

Java-klasser lastes inn av en forekomst av java.lang.ClassLoader. Klasselastere er imidlertid klasser selv. Derfor er spørsmålet hvem laster inn java.lang.ClassLoader seg selv?

Det er her bootstrap eller primordial class loader kommer inn i bildet.

Det er hovedsakelig ansvarlig for lasting av JDK interne klasser, vanligvis rt.jar og andre kjernebiblioteker i $ JAVA_HOME / jre / lib-katalogen. I tillegg Bootstrap class loader fungerer som foreldre til alle de andre ClassLoader tilfeller.

Denne bootstrap-klasselaster er en del av kjernen JVM og er skrevet i innfødt kode som påpekt i eksemplet ovenfor. Ulike plattformer kan ha forskjellige implementeringer av denne spesiallasteren.

2.2. Forlengerklasser

De extension class loader er et barn av bootstrap class loader og tar seg av å laste utvidelsene til de vanlige Java-klassene slik at den er tilgjengelig for alle applikasjoner som kjører på plattformen.

Lastere av utvidelsesklasse lastes vanligvis fra JDK-utvidelseskatalogen $ JAVA_HOME / lib / ekst katalog eller annen katalog som er nevnt i java.ext.dirs systemegenskap.

2.3. Systemklasselaster

Systemet eller applikasjonsklasselasteren, derimot, tar seg av å laste alle applikasjonsnivåklassene inn i JVM. Den laster inn filer som finnes i classpath-miljøvariabelen, -klassesti eller -cp kommandolinjealternativ. Det er også et barn av Extensions classloader.

3. Hvordan fungerer klasselaster?

Klasselastere er en del av Java Runtime Environment. Når JVM ber om en klasse, prøver klasselaster å finne klassen og laste klassedefinisjonen inn i kjøretiden med det fullstendige klassenavnet.

De java.lang.ClassLoader.loadClass () metoden er ansvarlig for å laste klassedefinisjonen inn i kjøretid. Den prøver å laste klassen basert på et fullt kvalifisert navn.

Hvis klassen ikke allerede er lastet, delegerer den forespørselen til overordnet klasselaster. Denne prosessen skjer rekursivt.

Til slutt, hvis foreldreklasselaster ikke finner klassen, vil barneklassen ringe java.net.URLClassLoader.findClass () metode for å se etter klasser i selve filsystemet.

Hvis den siste barneklasser ikke er i stand til å laste klassen heller, kaster den det java.lang.NoClassDefFoundError eller java.lang.ClassNotFoundException.

La oss se på et eksempel på utdata når ClassNotFoundException kastes.

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

Hvis vi går gjennom hendelsesforløpet rett fra å ringe java.lang.Class.forName (), kan vi forstå at den først prøver å laste klassen gjennom foreldreklasselaster og deretter java.net.URLClassLoader.findClass () å lete etter klassen selv.

Når den fremdeles ikke finner klassen, kaster den en ClassNotFoundException.

Det er tre viktige trekk ved klasselastere.

3.1. Delegasjonsmodell

Klasselastere følger delegasjonsmodellen hvor på forespørsel å finne en klasse eller ressurs, a ClassLoader forekomst vil delegere søket i klassen eller ressursen til overordnet klasselaster.

La oss si at vi har en forespørsel om å laste en applikasjonsklasse inn i JVM. Systemklasselaster delegerer først innlastingen av den klassen til den overordnede utvidelseklasselasteren, som igjen delegerer den til bootstrap-klasselaster.

Bare hvis bootstrap og deretter utvidelsesklasselaster mislykkes i å laste klassen, prøver systemklasselaster å laste selve klassen.

3.2. Unike klasser

Som en konsekvens av delegasjonsmodellen er det enkelt å sikre unike klasser da vi alltid prøver å delegere oppover.

Hvis foreldreklasselaster ikke finner klassen, vil bare den nåværende forekomsten prøve å gjøre det selv.

3.3. Synlighet

I tillegg, barneklasser er synlige for klasser som er lastet av foreldrene.

For eksempel har klasser lastet av systemklasselaster synlighet i klasser lastet av utvidelses- og Bootstrap-klasselaster, men ikke omvendt.

For å illustrere dette, hvis klasse A lastes av en applikasjonsklasselaster og klasse B lastes av utvidelsesklasselaster, så er både A- og B-klassene synlige så langt som andre klasser lastet av applikasjonsklasselaster.

Klasse B er likevel den eneste klassen som er synlig når det gjelder andre klasser lastet av forlengelsesklasselaster.

4. Tilpasset ClassLoader

Den innebygde klasselasteren vil være tilstrekkelig i de fleste tilfeller der filene allerede er i filsystemet.

I scenarier der vi trenger å laste klasser ut av den lokale harddisken eller et nettverk, kan vi imidlertid trenge å bruke tilpassede klasselastere.

I denne delen vil vi dekke noen andre bruksområder for tilpassede klasselastere, og vi vil demonstrere hvordan du lager en.

4.1. Brukertilpassede lastelastere

Tilpassede klasselastere er nyttige for mer enn bare å laste klassen i løpet av kjøretiden, noen få bruksområder kan omfatte:

  1. Hjelper med å modifisere den eksisterende bytekoden, f.eks. vevemidler
  2. Opprette klasser som er dynamisk tilpasset brukerens behov. For eksempel i JDBC, byttes det mellom forskjellige driverimplementasjoner gjennom dynamisk klasselasting.
  3. Implementering av en versjonsmekanisme for klassen mens du laster inn forskjellige bytekoder for klasser med samme navn og pakker. Dette kan gjøres enten gjennom URL-klasselaster (lastekrukker via URL-er) eller tilpassede klasselastere.

Det er flere konkrete eksempler der tilpassede klasselastere kan komme til nytte.

Nettlesere bruker for eksempel en tilpasset klasselaster for å laste inn kjørbart innhold fra et nettsted. En nettleser kan laste inn appletter fra forskjellige websider ved hjelp av separate klasselaster. Appletvisningen som brukes til å kjøre applets inneholder en ClassLoader som får tilgang til et nettsted på en ekstern server i stedet for å se i det lokale filsystemet.

Og deretter laster de rå bytekodefilene via HTTP, og gjør dem til klasser inne i JVM. Selv om disse applets har samme navn, de betraktes som forskjellige komponenter hvis de lastes av forskjellige klasselastere.

Nå som vi forstår hvorfor tilpassede klasselastere er relevante, la oss implementere en underklasse av ClassLoader for å utvide og oppsummere funksjonaliteten til hvordan JVM laster inn klasser.

4.2. Lage vår tilpassede klasselaster

For illustrasjonsformål, la oss si at vi må laste klasser fra en fil ved hjelp av en tilpasset klasselaster.

Vi må utvide ClassLoader klasse og overstyre findClass () metode:

offentlig klasse CustomClassLoader utvider ClassLoader {@Override public Class findClass (String name) kaster ClassNotFoundException {byte [] b = loadClassFromFile (name); return defineClass (navn, b, 0, b.lengde); } privat byte [] loadClassFromFile (String fileName) {InputStream inputStream = getClass (). getClassLoader (). getResourceAsStream (fileName.replace ('.', File.separatorChar) + ".class"); byte [] buffer; ByteArrayOutputStream byteStream = ny ByteArrayOutputStream (); int nextValue = 0; prøv {while ((nextValue = inputStream.read ())! = -1) {byteStream.write (nextValue); }} fange (IOException e) {e.printStackTrace (); } buffer = byteStream.toByteArray (); returbuffer; }}

I eksemplet ovenfor definerte vi en tilpasset klasselaster som utvider standard klasselaster og laster en byte-matrise fra den angitte filen.

5. Forståelse java.lang.ClassLoader

La oss diskutere noen viktige metoder fra java.lang.ClassLoader klasse for å få et klarere bilde av hvordan det fungerer.

5.1. De loadClass () Metode

offentlig klasse loadClass (strengnavn, boolsk løsning) kaster ClassNotFoundException {

Denne metoden er ansvarlig for å laste klassen gitt et navneparameter. Navneparameteren refererer til det fullt kvalifiserte klassenavnet.

Java Virtual Machine påkaller loadClass () metode for å løse klassereferanser innstilling løse til ekte. Det er imidlertid ikke alltid nødvendig å løse en klasse. Hvis vi bare trenger å avgjøre om klassen eksisterer eller ikke, er parameteren resolve satt til falsk.

Denne metoden fungerer som inngangspunkt for klasselaster.

Vi kan prøve å forstå det interne arbeidet til loadClass () metode fra kildekoden til java.lang.ClassLoader:

beskyttet Class loadClass (strengnavn, boolsk løsning) kaster ClassNotFoundException {synkronisert (getClassLoadingLock (navn)) {// Sjekk først om klassen allerede er lastet Class c = findLoadedClass (name); hvis (c == null) {lang t0 = System.nanoTime (); prøv {if (parent! = null) {c = parent.loadClass (name, false); } annet {c = findBootstrapClassOrNull (navn); }} fangst (ClassNotFoundException e) {// ClassNotFoundException kastet hvis klasse ikke ble funnet // fra den ikke-null overordnede klasselasteren} hvis (c == null) {// Hvis fremdeles ikke funnet, så påkalle findClass i rekkefølge // til finn klassen. c = findClass (navn); }} if (løse) {løse Klasse (c); } returnere c; }}

Standardimplementeringen av metoden søker etter klasser i følgende rekkefølge:

  1. Påkaller findLoadedClass (streng) metode for å se om klassen allerede er lastet.
  2. Påkaller loadClass (streng) metode på foreldreklasselaster.
  3. Påkalle findClass (streng) metode for å finne klassen.

5.2. De defineClass () Metode

beskyttet endelig Class defineClass (strengnavn, byte [] b, int av, int len) kaster ClassFormatError

Denne metoden er ansvarlig for konvertering av en rekke byte til en forekomst av en klasse. Og før vi bruker klassen, må vi løse det.

I tilfelle data ikke inneholder en gyldig klasse, kaster den a ClassFormatError.

Vi kan heller ikke overstyre denne metoden siden den er merket som endelig.

5.3. De findClass () Metode

beskyttet Class findClass (strengnavn) kaster ClassNotFoundException

Denne metoden finner klassen med det fullstendige navnet som parameter. Vi må overstyre denne metoden i tilpassede klasselasterimplementeringer som følger delegeringsmodellen for lasting av klasser.

Også, loadClass () påkaller denne metoden hvis foreldreklasselaster ikke kunne finne den valgte klassen.

Standardimplementeringen kaster a ClassNotFoundException hvis ingen foreldre til klasselaster finner klassen.

5.4. De getParent () Metode

offentlig finale ClassLoader getParent ()

Denne metoden returnerer overordnet klasselaster for delegering.

Noen implementeringer som den som er sett før i avsnitt 2. bruker null å representere bootstrap class loader.

5.5. De getResource () Metode

offentlig URL getResource (strengnavn)

Denne metoden prøver å finne en ressurs med gitt navn.

Den vil først delegere til foreldreklasselaster for ressursen. Hvis foreldrene er det null, blir det søkt etter banen til klasselaster som er innebygd i den virtuelle maskinen.

Hvis det mislykkes, vil metoden påberope seg findResource (streng) for å finne ressursen. Ressursnavnet som er angitt som inndata, kan være relativt eller absolutt i forhold til klassestien.

Den returnerer et URL-objekt for å lese ressursen, eller null hvis ressursen ikke ble funnet, eller hvis innkaller ikke har tilstrekkelige privilegier for å returnere ressursen.

Det er viktig å merke seg at Java laster inn ressurser fra klassestien.

Endelig, ressursinnlasting i Java regnes som uavhengig av sted da det ikke betyr noe hvor koden kjører så lenge miljøet er satt til å finne ressursene.

6. Context Classloaders

Generelt gir kontekstklasselastere en alternativ metode til klasselaseringsdelegeringsordningen introdusert i J2SE.

Som vi har lært før, klasselastere i en JVM følger en hierarkisk modell slik at hver klasselaster har en enslig forelder med unntak av bootstrap klasselaster.

Noen ganger når JVM-kjerneklasser trenger å laste inn klasser eller ressurser som leveres av applikasjonsutviklere, kan vi imidlertid støte på et problem.

For eksempel er kjernefunksjonaliteten i JNDI implementert av bootstrap-klasser i rt.jar. Men disse JNDI-klassene kan laste JNDI-leverandører implementert av uavhengige leverandører (distribuert i programklassen). Dette scenariet krever at bootstrap-klasselaster (overordnet klasselaster) skal laste inn en klasse som er synlig for applikasjonslasteren (underordnet klasselaster).

J2SE-delegasjonen fungerer ikke her, og for å omgå dette problemet, må vi finne alternative måter å laste klassen på. Og det kan oppnås ved hjelp av trådkontekstlastere.

De java.lang.Tråd klasse har en metode getContextClassLoader () som returnerer ContextClassLoader for den aktuelle tråden. De ContextClassLoader leveres av skaperen av tråden når du laster inn ressurser og klasser.

Hvis verdien ikke er angitt, er den standard klasselaster-konteksten til overordnet tråd.

7. Konklusjon

Klasselastere er avgjørende for å utføre et Java-program. Vi har gitt en god introduksjon som en del av denne artikkelen.

Vi snakket om forskjellige typer klasselaster, nemlig Bootstrap, Extensions og System class loaders. Bootstrap fungerer som foreldre for dem alle og er ansvarlig for å laste JDK interne klasser. Utvidelser og system laster derimot klasser fra henholdsvis Java-utvidelseskatalogen og klassestien.

Så snakket vi om hvordan klasselastere fungerer, og vi diskuterte noen funksjoner som delegering, synlighet og unikhet, etterfulgt av en kort forklaring på hvordan du lager en tilpasset. Til slutt ga vi en introduksjon til Context class loaders.

Kodeeksempler, som alltid, kan du finne på GitHub.


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