Java Language
Classloaders
Sök…
Anmärkningar
En klasslastare är en klass vars främsta syfte är att förmedla platsen och lastningen av klasser som används av en applikation. En klasslastare kan också hitta och ladda resurser .
Standardklasserna kan ladda klasser och resurser från kataloger i filsystemet och från JAR- och ZIP-filer. De kan också ladda ner och cache JAR- och ZIP-filer från en fjärrserver.
Klasslastare är vanligtvis kedjade, så att JVM kommer att försöka ladda klasser från standardklassbiblioteken företräde framför applikationskällor. Anpassade klasslastare tillåter programmeraren att ändra detta. Den kan också göra sådana saker som dekryptera bytecode-filer och bytecode-modifiering.
Instantiera och använda en klasslastare
Detta grundexempel visar hur en applikation kan instansera en klasslastare och använda den för att dynamiskt ladda en klass.
URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");
Klassläsaren som skapats i detta exempel har standardklassläsaren som överordnad och kommer först att försöka hitta någon klass i överordnad klasslaster innan du tittar på "extra.jar". Om den begärda klassen redan har laddats findClass
samtalet referensen till den tidigare laddade klassen.
findClass
samtalet kan misslyckas på många olika sätt. De vanligaste är:
- Om den namngivna klassen inte hittas, samtalet med throw
ClassNotFoundException
. - Om den nämnda klassen beror på någon annan klass som inte kan hittas, kommer samtalet att kasta
NoClassDefFoundError
.
Implementera en anpassad klassLoader
Varje anpassad lastare måste direkt eller indirekt utöka klassen java.lang.ClassLoader
. De viktigaste förlängningspunkterna är följande metoder:
-
findClass(String)
- överbelasta denna metod om din klasslastare följer standarddelegationsmodellen för klassbelastning. -
loadClass(String, boolean)
- överbelastning av denna metod för att implementera en alternativ delegeringsmodell. -
findResource
ochfindResources
- överbelasta dessa metoder för att anpassa resursbelastning.
defineClass
metoderna som ansvarar för att faktiskt ladda klassen från en byte-grupp är final
att förhindra överbelastning. Alla anpassade beteenden måste utföras innan du defineClass
.
Här är en enkel som laddar en specifik klass från en byte-grupp:
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);
}
}
}
Eftersom vi bara har åsidosatt metoden findClass
kommer denna anpassade klasslastare att fungera enligt följande när loadClass
anropas.
- Metoden klasslastarens
loadClass
anroparfindLoadedClass
att se om en klass med det här namnet redan har laddats av denna klasslastare. Om det lyckas, det resulterandeClass
är föremål tillbaka till den begärande. -
loadClass
metoden delegerar sedan till överordnade klasslastare genom att ringa sittloadClass
samtal. Om föräldern kan ta itu med begäran kommer det returnera enClass
föremål som sedan returneras till förfrågaren. - Om föräldrarnas klassläsare inte kan ladda klassen, kallar
findClass
sedan vår åsidosättande metod förfindClass
och passerar namnet på klassen som ska laddas. - Om det begärda namnet stämmer med
this.classname
, kallar videfineClass
att ladda den faktiska klassen frånthis.classfile
byte-array. Den resulterandeClass
objektet returneras sedan. - Om namnet inte matchade kastar vi
ClassNotFoundException
.
Laddar en extern .klassfil
För att ladda en klass måste vi först definiera den. Klassen definieras av ClassLoader
. Det finns bara ett problem, Oracle skrev inte ClassLoader
koden med den här funktionen tillgänglig. För att definiera klassen måste vi komma åt en metod med namnet defineClass()
som är en privat metod för ClassLoader
.
För att komma åt det, vad vi kommer att göra är att skapa en ny klass, ByteClassLoader
, och utöka den till ClassLoader
. Nu ClassLoader
vi har utökat vår klass till ClassLoader
kan vi få tillgång till ClassLoader
privata metoder. För att göra defineClass()
tillgängligt kommer vi att skapa en ny metod som fungerar som en spegel för den privata defineClass()
-metoden. För att ringa den privata metoden kommer vi att behöva klassnamnet, name
, classBytes
, classBytes
, den första byteförskjutningen, som kommer att vara 0
eftersom classBytes
börjar på classBytes[0]
, och den sista byteförskjutningen, som kommer att vara classBytes.lenght
eftersom den representerar storleken på data, som kommer att vara den sista offset.
public class ByteClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] classBytes) {
return defineClass(name, classBytes, 0, classBytes.length);
}
}
Nu har vi en offentlig defineClass()
-metod. Det kan kallas genom att lämna klassens namn och klassbyte som argument.
Låt oss säga att vi har klass som heter MyClass
i paketet stackoverflow
...
För att kalla metoden behöver vi klassbyte så vi skapar ett Path
representerar vår Paths.get()
genom att använda Paths.get()
och passera banan för den binära klassen som ett argument. Nu kan vi få Files.readAllBytes(path)
med Files.readAllBytes(path)
. Så vi skapar en ByteClassLoader
instans och använder metoden vi skapade, defineClass()
. Vi har redan klassbyte men för att kalla vår metod behöver vi också klassnamnet som ges av paketnamnet (dot) klassens kanoniska namn, i detta fall stackoverflow.MyClass
.
Path path = Paths.get("MyClass.class");
ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);
Obs defineClass()
Metoden defineClass()
returnerar ett Class<?>
-Objekt. Du kan spara det om du vill.
För att ladda klassen ringer vi bara loadClass()
och loadClass()
. Den här metoden kan kasta en ClassNotFoundException
så vi måste använda ett test cath-block
try{
loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
e.printStackTrace();
}