To nie odpowiada na pierwotne pytanie, ale ponieważ pytanie jest wysoko ocenione i powiązane z każdym ContextClassLoader
zapytaniem, myślę, że ważne jest, aby odpowiedzieć na powiązane pytanie, kiedy należy użyć modułu ładującego klasy kontekstu. Krótka odpowiedź: nigdy nie używaj modułu ładującego klasy kontekstu ! Ale ustaw tę getClass().getClassLoader()
opcję, gdy trzeba wywołać metodę, w której brakuje ClassLoader
parametru.
Gdy kod z jednej klasy prosi o załadowanie innej klasy, właściwym modułem ładującym jest ten sam moduł ładujący co klasa wywołująca (tj getClass().getClassLoader()
.). W ten sposób działa 99,9% czasu, ponieważ JVM robi to samo przy pierwszym utworzeniu instancji nowej klasy, wywołaniu metody statycznej lub uzyskaniu dostępu do pola statycznego.
Jeśli chcesz utworzyć klasę za pomocą odbicia (na przykład podczas deserializacji lub ładowania konfigurowalnej nazwanej klasy), biblioteka wykonująca odbicie powinna zawsze pytać aplikację, który moduł ładujący klasy ma użyć, otrzymując ClassLoader
jako parametr od aplikacji. Aplikacja (która zna wszystkie klasy wymagające budowy) powinna ją przekazać getClass().getClassLoader()
.
Każdy inny sposób uzyskania modułu ładującego klasy jest niepoprawny. Jeśli biblioteka używa hacków takich jak Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
lub sun.reflect.Reflection.getCallerClass()
jest to błąd spowodowany brakiem interfejsu API. Zasadniczo Thread.getContextClassLoader()
istnieje tylko dlatego, że ktokolwiek zaprojektował ObjectInputStream
API, zapomniał zaakceptować ten ClassLoader
parametr, a ten błąd nawiedził społeczność Java do dziś.
To powiedziawszy, wiele wielu klas JDK używa jednego z kilku hacków, aby odgadnąć jakiś moduł ładujący klasy. Niektóre używają ContextClassLoader
(co kończy się niepowodzeniem, gdy uruchamiasz różne aplikacje we współużytkowanej puli wątków lub gdy opuszczasz ContextClassLoader null
), niektóre chodzą po stosie (co kończy się niepowodzeniem, gdy bezpośredni obiekt wywołujący klasę jest biblioteką), niektóre używają modułu ładującego klasy systemowe (co jest w porządku, pod warunkiem, że jest udokumentowane, aby używać tylko klas w CLASSPATH
module ładującym) lub w programie ładującym klasy bootstrap, a niektóre używają nieprzewidywalnej kombinacji powyższych technik (co tylko bardziej komplikuje sprawę). Spowodowało to dużo płaczu i zgrzytania zębami.
Korzystając z takiego interfejsu API, najpierw spróbuj znaleźć przeciążenie metody, która akceptuje moduł ładujący klasę jako parametr . Jeśli nie ma rozsądnej metody, spróbuj ustawić ContextClassLoader
przed wywołaniem interfejsu API (i zresetować go później):
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
// call some API that uses reflection without taking ClassLoader param
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
ClassB
musi to być ścieżka klasy programuClassA
ładującego (lubClassA
rodziców programu ładującego)? Czy nie jest możliwe, abyClassA
moduł ładujący nadpisałloadClass()
, tak że można go pomyślnie załadować,ClassB
nawet jeśliClassB
nie ma ścieżki klas?