Найти, откуда загружается класс 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


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

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/

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


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


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
Нет, это не так. Загрузчик классов может на самом деле использовать совершенно другой путь к классу - это означает, что он не сможет полностью определить фактическое местоположение класса.
Томаш Зато - Восстановить Монику
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.