Кто-нибудь знает, как программно узнать, откуда загрузчик классов java действительно загружает класс?
Я часто работаю над большими проектами, где путь к классам становится очень длинным, а ручной поиск - не вариант. Недавно у меня был проблема, где загрузчик классов загружал неправильную версию класса, потому что он находился в пути к классам в двух разных местах.
Итак, как я могу заставить загрузчик классов сказать мне, откуда на диске находится фактический файл класса?
Редактировать: Что насчет того, если загрузчик классов действительно не может загрузить класс из-за несовпадения версий (или чего-то еще), можем ли мы каким-либо образом узнать, какой файл он пытается прочитать, прежде чем он его прочитает?




Предполагая, что вы работаете с классом MyClass, должно работать следующее:
MyClass.class.getClassLoader();
Сможете ли вы получить расположение файла .class на диске, зависит от самого загрузчика классов. Например, если вы используете что-то вроде BCEL, определенный класс может даже не иметь представления на диске.
Нет, это не так. Загрузчик классов может ссылаться на совершенно другой путь к классам - это означает, что он не сможет определить фактическое местоположение класса.
Взгляните на этот похожий вопрос. Инструмент для открытия того же класса ..
Я думаю, что наиболее важным препятствием является наличие собственного загрузчика классов (загрузка из db или ldap)
Вот пример:
package foo;
public class Test
{
public static void main(String[] args)
{
ClassLoader loader = Test.class.getClassLoader();
System.out.println(loader.getResource("foo/Test.class"));
}
}
Это распечатано:
file:/C:/Users/Jon/Test/foo/Test.class
Чтобы сократить избыточную типизацию, можно также использовать более короткую версию: Test.class.getResource("Test.class"), в которой не повторяется имя пакета.
Что делать, если класс скомпилирован, например из файла .groovy?
@meriton: Или, чтобы выжить refactorinsgs: Test.class.getResource(Test.class.getSimpleName() + ".class")
Однако для BouncyCastleProvider требуется полное имя пакета.
getClassLoader() может вернуть null. См. здесь для расширения этого метода, чтобы справиться с этим.
@meriton Class#getResource(String name) делегирует загрузчику классов объекта, то есть методу ClassLoader#getResource(String name).
Также getResource может возвращать null. Этот код не будет работать каждый раз.
@ TomášZato: Это будет работать, если класс доступен загрузчику классов. Я согласен с тем, что если OP должен справиться с возможностью того, что класс недоступен, им нужно будет выполнить нулевую проверку - хотя это простая модификация.
Я использовал это для загрузки ресурсов, и здесь это не удалось. В частности, это не удалось, когда я запускал тестовый класс Maven - в этот момент класс в моем проекте пытался загрузить ресурсы из /target/test-classes вместо /target/classes. Проблема была решена с помощью new MyClass().getClass().getResource("...") - или просто this.getClass ..., если в конкретном контексте.
getClass().getProtectionDomain().getCodeSource().getLocation();
Да, хотя он не работает с установленным менеджером безопасности и без необходимых разрешений.
К вашему сведению, NPE = исключение нулевого указателя. HTH!
Этот метод предпочтительнее, если у вас есть ссылка на экземпляр, поскольку вы можете загрузить один и тот же класс из двух разных мест.
Также не работает при вызове из модуля Java 9+ (о котором, конечно, вы не могли знать в 2008 году).
Вот что мы используем:
public static String getClassResource(Class<?> klass) {
return klass.getClassLoader().getResource(
klass.getName().replace('.', '/') + ".class").toString();
}
Это будет работать в зависимости от реализации ClassLoader:
getClass().getProtectionDomain().getCodeSource().getLocation()
Другой способ узнать, откуда загружается класс (без изменения источника), - запустить виртуальную машину Java с опцией: -verbose:class
это сработало очень хорошо и не имеет проблемы с классами с нулевым ClassLoader
@ries Если не нужно делать это программно, это определенно правильный путь, и он решил мою проблему. Однако OP конкретно спросил, как это сделать программно.
Версия Джона терпит неудачу, когда объект ClassLoader зарегистрирован как null, что, кажется, подразумевает, что он был загружен загрузочным ClassLoader.
Этот метод решает эту проблему:
public static String whereFrom(Object o) {
if ( o == null ) {
return null;
}
Class<?> c = o.getClass();
ClassLoader loader = c.getClassLoader();
if ( loader == null ) {
// Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
loader = ClassLoader.getSystemClassLoader();
while ( loader != null && loader.getParent() != null ) {
loader = loader.getParent();
}
}
if (loader != null) {
String name = c.getCanonicalName();
URL resource = loader.getResource(name.replace(".", "/") + ".class");
if ( resource != null ) {
return resource.toString();
}
}
return "Unknown";
}
Отредактируйте только 1-ю строку: Main.class
Class<?> c = Main.class;
String path = c.getResource(c.getSimpleName() + ".class").getPath().replace(c.getSimpleName() + ".class", "");
System.out.println(path);
Выход:
/C:/Users/Test/bin/
Может быть, плохой стиль, но отлично работает!
Этот подход работает как для файлов, так и для банок:
Class clazz = Class.forName(nameOfClassYouWant);
URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class");
InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish
Как правило, мы не знаем, что использовать при жестком кодировании. Мы можем сначала получить имя класса, а затем использовать ClassLoader для получения URL-адреса класса.
String className = MyClass.class.getName().replace(".", "/")+".class";
URL classUrl = MyClass.class.getClassLoader().getResource(className);
String fullPath = classUrl==null ? null : classUrl.getPath();
Должен быть: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
MyClass.class - важная часть - getClass () может возвращать прокси! Затем вы можете получить имя типа MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class и нулевой URL.
Простой способ:
System.out.println(java.lang.String.class.getResource(String.class.getSimpleName()+".class"));
Наш пример:
jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class
Или же
String obj = "simple test"; System.out.println(obj.getClass().getResource(obj.getClass().getSimpleName()+".class"));
Наш пример:
jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class
Это возвращает ClassLoader, используемый для загрузки класса, не так ли? Не находит где файл .class?