Szukaj…


Uwagi

Classloader to klasa, której głównym celem jest pośredniczenie w lokalizacji i ładowaniu klas używanych przez aplikację. Moduł ładujący klasy może także znajdować i ładować zasoby .

Standardowe klasy modułu ładującego klasy mogą ładować klasy i zasoby z katalogów w systemie plików oraz z plików JAR i ZIP. Mogą także pobierać i buforować pliki JAR i ZIP ze zdalnego serwera.

Programy ładujące klasy są zwykle połączone łańcuchem, aby JVM spróbowała załadować klasy ze standardowych bibliotek klas zamiast źródeł dostarczonych przez aplikację. Niestandardowe moduły ładujące pozwalają programiście to zmienić. Mogą także wykonywać takie czynności, jak odszyfrowanie plików kodu bajtowego i modyfikacja kodu bajtowego.

Tworzenie instancji i korzystanie z modułu ładującego klasy

Ten podstawowy przykład pokazuje, jak aplikacja może utworzyć instancję modułu ładującego klasy i użyć go do dynamicznego ładowania klasy.

URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");

Moduł ładujący utworzony w tym przykładzie będzie miał domyślny moduł ładujący jako swój nadrzędny i najpierw spróbuje znaleźć dowolną klasę w nadrzędnym module ładującym, zanim przejdzie do „extra.jar”. Jeśli żądana klasa została już załadowana, wywołanie findClass zwróci odwołanie do poprzednio załadowanej klasy.

Wywołanie findClass może zakończyć się niepowodzeniem na różne sposoby. Najczęstsze to:

  • Jeśli nie można znaleźć nazwanej klasy, wywołanie z ClassNotFoundException throw ClassNotFoundException .
  • Jeśli nazwana klasa zależy od innej klasy, której nie można znaleźć, wywołanie wyrzuci NoClassDefFoundError .

Implementowanie niestandardowego modułu ładującego klasy

Każdy niestandardowy moduł ładujący musi bezpośrednio lub pośrednio rozszerzyć klasę java.lang.ClassLoader . Głównymi punktami rozszerzenia są następujące metody:

  • findClass(String) - przeciąż tę metodę, jeśli findClass(String) klasy postępuje zgodnie ze standardowym modelem delegowania do ładowania klas.
  • loadClass(String, boolean) - przeciąż tę metodę, aby zaimplementować alternatywny model delegowania.
  • findResource i findResources - przeciąż te metody, aby dostosować ładowanie zasobów.

Metody defineClass , które są odpowiedzialne za ładowanie klasy z tablicy bajtów, są final aby zapobiec przeciążeniu. Każde niestandardowe zachowanie należy wykonać przed wywołaniem funkcji defineClass .

Oto proste ładowanie określonej klasy z tablicy bajtów:

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);
        }
    }
}

Ponieważ tylko findClass metodę findClass , ten moduł ładujący klasy niestandardowej będzie zachowywał się w następujący sposób po loadClass .

  1. Metoda findLoadedClass klasy loadClass wywołuje findLoadedClass aby sprawdzić, czy klasa o tej nazwie została już załadowana przez ten loadloader. Jeśli to się powiedzie, wynikowy obiekt Class jest zwracany do requestera.
  2. Metoda loadClass deleguje następnie do nadrzędnego loadClass klasy, wywołując wywołanie loadClass . Jeśli rodzic może poradzić sobie z żądaniem, zwróci obiekt Class który następnie zostanie zwrócony do żądającego.
  3. Jeśli nadrzędny findClass nie może załadować klasy, findClass wywołuje następnie naszą metodę zastępowania findClass , przekazując nazwę klasy do załadowania.
  4. Jeśli żądana nazwa pasuje do this.classname , wywołujemy defineClass aby załadować rzeczywistą klasę z tablicy bajtów this.classfile . Wynikowy obiekt Class jest następnie zwracany.
  5. Jeśli nazwa się nie zgadza, ClassNotFoundException .

Ładowanie zewnętrznego pliku .class

Aby załadować klasę, musimy ją najpierw zdefiniować. Klasa jest zdefiniowana przez ClassLoader . Jest tylko jeden problem, Oracle nie napisał kodu ClassLoader z tą dostępną funkcją. Aby zdefiniować klasę, musimy uzyskać dostęp do metody o nazwie defineClass() która jest prywatną metodą ClassLoader .

Aby uzyskać do niego dostęp, utworzymy nową klasę ByteClassLoader i rozszerzymy ją na ClassLoader . Teraz, gdy rozszerzyliśmy naszą klasę na ClassLoader , możemy uzyskać dostęp do prywatnych metod ClassLoader . Aby udostępnić defineClass() , stworzymy nową metodę, która będzie działać jak lustro dla prywatnej metody defineClass() . Aby wywołać metodę prywatną, potrzebujemy nazwy klasy, name , bajtów klasy, classBytes , przesunięcia pierwszego bajtu, który będzie classBytes 0 ponieważ dane classBytes[0] zaczynają się od classBytes[0] , i przesunięcia ostatniego bajtu, który będzie classBytes.lenght ponieważ reprezentuje rozmiar danych, który będzie ostatnim przesunięciem.

public class ByteClassLoader extends ClassLoader {

    public Class<?> defineClass(String name, byte[] classBytes) {
        return defineClass(name, classBytes, 0, classBytes.length);
    }

}

Teraz mamy publiczną defineClass() . Można go wywołać, przekazując nazwę klasy i bajty klasy jako argumenty.

Powiedzmy, że mamy klasę o nazwie MyClass w pakiecie stackoverflow ...

Aby wywołać metodę, potrzebujemy bajtów klasy, dlatego tworzymy obiekt Path reprezentujący Path naszej klasy za pomocą metody Paths.get() i przekazując ścieżkę klasy binarnej jako argument. Teraz możemy pobrać bajty klasy za pomocą Files.readAllBytes(path) . Tworzymy ByteClassLoader instancję ByteClassLoader i korzystamy z utworzonej przez nas metody, defineClass() . Mamy już bajty klasy, ale aby wywołać naszą metodę, potrzebujemy również nazwy klasy, która jest podana przez nazwę pakietu (kropka) kanoniczną nazwą klasy, w tym przypadku stackoverflow.MyClass .

Path path = Paths.get("MyClass.class");

ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);

Uwaga : Metoda defineClass() zwraca obiekt Class<?> . Możesz go zapisać, jeśli chcesz.

Aby załadować klasę, po prostu wywołujemy loadClass() i przekazujemy nazwę klasy. Ta metoda może ClassNotFoundException dlatego musimy użyć bloku try cath

try{
    loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
    e.printStackTrace();
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow