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 och findResources - ö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.

  1. Metoden klasslastarens loadClass anropar findLoadedClass att se om en klass med det här namnet redan har laddats av denna klasslastare. Om det lyckas, det resulterande Class är föremål tillbaka till den begärande.
  2. loadClass metoden delegerar sedan till överordnade klasslastare genom att ringa sitt loadClass samtal. Om föräldern kan ta itu med begäran kommer det returnera en Class föremål som sedan returneras till förfrågaren.
  3. Om föräldrarnas klassläsare inte kan ladda klassen, kallar findClass sedan vår åsidosättande metod för findClass och passerar namnet på klassen som ska laddas.
  4. Om det begärda namnet stämmer med this.classname , kallar vi defineClass att ladda den faktiska klassen från this.classfile byte-array. Den resulterande Class objektet returneras sedan.
  5. 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();
}


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow