Найти, откуда загружается класс Java

184

Кто-нибудь знает, как программно узнать, откуда на самом деле загрузчик классов Java загружает класс?

Я часто работаю над большими проектами, в которых путь к классам становится очень длинным, а поиск вручную не всегда возможен. У меня недавно была проблема, когда загрузчик классов загружал неверную версию класса, потому что он находился в пути к классам в двух разных местах.

Итак, как я могу заставить загрузчик классов сказать мне, откуда на диске берется фактический файл класса?

Редактировать: А как насчет того, если загрузчик классов действительно не может загрузить класс из-за несоответствия версий (или чего-то еще), есть ли в любом случае, мы могли бы узнать, какой файл он пытается прочитать, прежде чем он читает его?

Люк
источник
4
@JarrodRoberson Я не думаю, что это следует считать дубликатом stackoverflow.com/questions/11747833/… поскольку этот вопрос был задан в 2008 году, и этот вопрос был задан в 2012 году
Луки

Ответы:

189

Вот пример:

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
Джон Скит
источник
32
Чтобы сократить избыточную типизацию, можно также использовать более короткую версию:, Test.class.getResource("Test.class")которая не повторяет имя пакета.
меритон
1
Что если класс скомпилирован, например, из файла .groovy?
Ондра Жижка
35
@meriton: Или, чтобы выжить refactorinsgs:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy
1
Для BouncyCastleProviderполного имени пакета требуется однако.
Павел Власов
3
Можно getClassLoader()вернуться null. Смотрите здесь для расширения этого метода, чтобы справиться с этим.
OldCurmudgeon
100

Другой способ выяснить, откуда загружается класс (без манипулирования источником), - запустить Java VM с параметром: -verbose:class

jiriki
источник
6
это работало очень хорошо, и у него не было проблемы работы с классами с нулевым ClassLoader
lexicalscope
2
@ries Если кому-то не нужно делать это программно, это, безусловно, путь, и он решил мою проблему. Однако ОП специально спросил, как это сделать программно.
SantiBailors
80
getClass().getProtectionDomain().getCodeSource().getLocation();
Дейв ДиФранко
источник
4
Да, хотя он не работает с установленным менеджером безопасности и без необходимых разрешений.
Том Хотин - tackline
1
К вашему сведению, NPE = исключение нулевого указателя. НТН!
Евгений Сергеев
Этот метод предпочтителен, если у вас есть ссылка на экземпляр, поскольку вы можете загрузить один и тот же класс из двух разных мест.
Мигель Пинг
1
Также не работает при вызове из модуля Java 9+ (который, конечно, вы не могли знать в 2008 году).
Джефф Г
28

Это то, что мы используем:

public static String getClassResource(Class<?> klass) {
  return klass.getClassLoader().getResource(
     klass.getName().replace('.', '/') + ".class").toString();
}

Это будет работать в зависимости от реализации ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

Евгений Кабанов
источник
17

Версия Джона терпит неудачу, когда объект 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";
}
OldCurmudgeon
источник
5

Отредактируйте только 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/

Может быть, плохой стиль, но работает отлично!

ecerer
источник
3

Как правило, нам не нужно использовать жесткое кодирование. Сначала мы можем получить className, а затем использовать 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 () может вернуть Proxy! Затем вы можете получить имя, например MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class, и нулевой URL.
Джалмаси
1

Простой способ:

System.out.println (java.lang.String.class.getResource (String.class.getSimpleName () + "класс."));

Пример выхода:

баночка: файл / D: /Java/jdk1.8/jre/lib/rt.jar /java/lang/String.class

Или

String obj = "простой тест"; System.out.println (obj.getClass () getResource (obj.getClass () getSimpleName () +).. "Класс.");

Пример выхода:

баночка: файл / D: /Java/jdk1.8/jre/lib/rt.jar /java/lang/String.class

seduardo
источник
0

Этот подход работает как для файлов, так и для jar-файлов:

Class clazz = Class.forName(nameOfClassYouWant);

URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class");
InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish
Адам
источник
-1

Предполагая, что вы работаете с классом с именем MyClass, должно работать следующее:

MyClass.class.getClassLoader();

Доступность расположения файла .class на диске зависит от самого загрузчика классов. Например, если вы используете что-то вроде BCEL, определенный класс может даже не иметь представления на диске.

Даниэль Спивак
источник
Это возвращает ClassLoader, используемый для загрузки класса, не так ли? Не находит где файл .class?
Корай Тугай
1
Нет, это не так. Загрузчик классов может на самом деле использовать совершенно другой путь к классу - это означает, что он не сможет полностью определить фактическое местоположение класса.
Томаш Зато - Восстановить Монику