Java Language
klasse laders
Zoeken…
Opmerkingen
Een classloader is een klasse waarvan het primaire doel is om te bemiddelen bij de locatie en het laden van klassen die door een toepassing worden gebruikt. Een klassenlader kan ook bronnen zoeken en laden.
De standaard classloader-klassen kunnen klassen en bronnen laden uit mappen in het bestandssysteem en uit JAR- en ZIP-bestanden. Ze kunnen ook JAR- en ZIP-bestanden downloaden en opslaan in een cache van een externe server.
Classloaders zijn normaal gesproken geketend, zodat de JVM zal proberen klassen uit de standaardklassebibliotheken te laden in plaats van door de applicatie geleverde bronnen. Met aangepaste classloaders kan de programmeur dit wijzigen. Het kan ook dingen doen zoals het decoderen van bytecode-bestanden en bytecode-aanpassing.
Instantiëren en een classloader gebruiken
Dit basisvoorbeeld laat zien hoe een toepassing een classloader kan instantiëren en gebruiken om een klasse dynamisch te laden.
URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");
De classloader die in dit voorbeeld is gemaakt, heeft de standaardclassloader als bovenliggend element en probeert eerst elke klasse in de bovenliggende classloader te vinden voordat wordt gekeken naar "extra.jar". Als de gevraagde klasse al is geladen, findClass
aanroep findClass
de verwijzing naar de eerder geladen klasse.
Het gesprek findClass
kan op verschillende manieren mislukken. De meest voorkomende zijn:
- Als de genoemde klasse niet kan worden gevonden, de aanroep met throw
ClassNotFoundException
. - Als de genoemde klasse afhankelijk is van een andere klasse die niet kan worden gevonden, wordt
NoClassDefFoundError
.
Een aangepaste classLoader implementeren
Elke aangepaste lader moet de klasse java.lang.ClassLoader
direct of indirect uitbreiden. De belangrijkste uitbreidingspunten zijn de volgende methoden:
-
findClass(String)
- overbelast deze methode als uw classloader het standaard delegatiemodel volgt voor het laden van klassen. -
loadClass(String, boolean)
- overbelast deze methode om een alternatief delegatiemodel te implementeren. -
findResource
enfindResources
- overbelast deze methoden om het laden van bronnen aan te passen.
De defineClass
methoden die verantwoordelijk zijn voor het daadwerkelijk laden van de klasse uit een bytearray zijn final
om overbelasting te voorkomen. Eventueel aangepast gedrag moet worden uitgevoerd voordat defineClass
.
Hier is een eenvoudige die een specifieke klasse uit een byte-array laadt:
public class ByteArrayClassLoader extends ClassLoader {
private String classname;
private byte[] classfile;
public ByteArrayClassLoader(String classname, byte[] classfile) {
this.classname = classname;
this.classfile = classfile.clone();
}
@Override
protected Class findClass(String classname) throws ClassNotFoundException {
if (classname.equals(this.classname)) {
return defineClass(classname, classfile, 0, classfile.length);
} else {
throw new ClassNotFoundException(classname);
}
}
}
Omdat we alleen de methode findClass
hebben overschreven, findClass
deze aangepaste klassenlader zich als volgt wanneer loadClass
wordt aangeroepen.
- De classloader's
loadClass
methode oproepenfindLoadedClass
om te zien of een klasse met deze naam al door deze klasse loader is geladen. Als dat lukt, wordt het resulterendeClass
object teruggestuurd naar de aanvrager. - De methode
loadClass
delegeert vervolgens naar de bovenliggende classloader door de aanroeploadClass
roepen. Als de ouder het verzoek kan verwerken, retourneert deze eenClass
object dat vervolgens wordt teruggestuurd naar de aanvrager. - Als de bovenliggende classloader de klasse niet kan laden, roept
findClass
onze overridefindClass
methode aan, waarbij de naam van de te laden klasse wordtfindClass
. - Als de gevraagde naam overeenkomt met
this.classname
, roepen wedefineClass
aan om de werkelijke klasse uit dethis.classfile
defineClass
te laden. Het resulterendeClass
object wordt vervolgens geretourneerd. - Als de naam niet overeenkomt, gooien we
ClassNotFoundException
.
Een extern .class-bestand laden
Om een klasse te laden, moeten we deze eerst definiëren. De klasse wordt gedefinieerd door de ClassLoader
. Er is maar één probleem, Oracle heeft de ClassLoader
code niet geschreven met deze functie beschikbaar. Om de klasse te definiëren, hebben we toegang nodig tot een methode met de naam defineClass()
, een ClassLoader
van ClassLoader
.
Om toegang te krijgen, maken we een nieuwe klasse, ByteClassLoader
, en breiden deze uit naar ClassLoader
. Nu we onze klasse hebben uitgebreid naar ClassLoader
, hebben we toegang tot de ClassLoader
de ClassLoader
. Om defineClass()
beschikbaar te maken, maken we een nieuwe methode die als een spiegel zal fungeren voor de private methode defineClass()
. Tot het private methode zullen we de naam van de klasse, moet bellen name
, de klasse bytes, classBytes
, de eerste byte te compenseren, die zal worden 0
, omdat classBytes
data begint bij classBytes[0]
, en de laatste byte te compenseren, die zal worden classBytes.lenght
omdat dit de grootte van de gegevens vertegenwoordigt, die de laatste offset is.
public class ByteClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] classBytes) {
return defineClass(name, classBytes, 0, classBytes.length);
}
}
Nu hebben we een openbare methode defineClass()
. Het kan worden opgeroepen door de naam van de klasse en de klassebytes als argumenten door te geven.
Laten we zeggen dat we klasse MyClass
in het pakket stackoverflow
...
Om de methode aan te roepen hebben we de klasse-bytes nodig, dus maken we een Path
object dat het pad van onze klasse vertegenwoordigt door de methode Paths.get()
gebruiken en het pad van de binaire klasse als argument door te geven. Nu kunnen we de Files.readAllBytes(path)
met Files.readAllBytes(path)
. Dus maken we een ByteClassLoader
instantie en gebruiken we de methode die we hebben gemaakt, defineClass()
. We hebben al de class-bytes, maar om onze methode aan te roepen, hebben we ook de klassenaam nodig die wordt gegeven door de pakketnaam (punt), de canonieke naam van de klasse, in dit geval stackoverflow.MyClass
.
Path path = Paths.get("MyClass.class");
ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);
Opmerking : de methode defineClass()
retourneert een object Class<?>
. Je kunt het opslaan als je wilt.
Om de klasse te laden, roepen we gewoon loadClass()
en geven we de klassenaam door. Deze methode kan een ClassNotFoundException
dus we moeten een try cath-blok gebruiken
try{
loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
e.printStackTrace();
}