Це не дає відповіді на оригінальне запитання, але оскільки це питання високо ранговане та пов'язане для будь-якого ContextClassLoader
запиту, я думаю, що важливо відповісти на відповідне питання про те, коли слід використовувати навантажувач контекстного класу. Коротка відповідь: ніколи не використовуйте завантажувач контекстного класу ! Але встановіть це, getClass().getClassLoader()
коли вам доведеться викликати метод, у якого відсутній ClassLoader
параметр.
Коли код з одного класу вимагає завантажити інший клас, правильний завантажувач класів для використання - це той же завантажувач класів, що і клас виклику (тобто getClass().getClassLoader()
). Так працює 99,9% часу, тому що це те, що JVM робить сам при першому конструюванні екземпляра нового класу, виклику статичного методу або доступу до статичного поля.
Коли ви хочете створити клас за допомогою відображення (наприклад, при десеріалізації або завантаженні настроюваного імені класу), бібліотека, яка робить відображення, завжди повинна запитувати додаток, яким завантажувачем класів користуватися, отримуючи ClassLoader
як параметр від програми. Додаток (який знає всі класи, які потребують побудови) повинен пройти його getClass().getClassLoader()
.
Будь-який інший спосіб отримати навантажувач класу невірно. Якщо бібліотека використовує хаки, такі як Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
або sun.reflect.Reflection.getCallerClass()
це помилка, викликана дефіцитом в API. В основному, Thread.getContextClassLoader()
існує лише тому, що той, хто розробляв ObjectInputStream
API, забув прийняти цей ClassLoader
параметр, і ця помилка переслідувала спільноту Java донині.
Однак, багато хто з класів JDK використовують один із кількох хаків, щоб відгадати якийсь завантажувач класів. Деякі використовують ContextClassLoader
(що не вдається, коли ви запускаєте різні додатки в спільному пулі потоків або коли ви виходите з нього ContextClassLoader null
), деякі вигулюєте стек (який виходить з ладу, коли прямий абонент класу сам є бібліотекою), деякі використовують завантажувач системного класу (що добре, якщо документально використовувати лише класи в CLASSPATH
) або завантажувач класу завантажувача, а деякі використовують непередбачувану комбінацію вищезазначених методів (що лише робить речі більш заплутаними). Це призвело до сильного плачу та скрегіт зубів.
Використовуючи такий API, спершу спробуйте знайти перевантаження методу, який приймає завантажувач класу як параметр . Якщо немає розумного методу, спробуйте встановити ContextClassLoader
до виклику API (і скинути його після цього):
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
повинно бути на уроціClassA
навантажувача (абоClassA
батьків навантажувача)? Хіба не можнаClassA
перевантажувати навантажувачloadClass()
таким чином, щоб він міг успішно завантажуватисяClassB
навіть тоді, колиClassB
він не перебуває на своєму шляху?