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

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

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

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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
196
0
182 474
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

MyClass.class.getClassLoader();

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

Это возвращает ClassLoader, используемый для загрузки класса, не так ли? Не находит где файл .class?

Koray Tugay 26.09.2014 09:46

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

Tomáš Zato - Reinstate Monica 07.03.2015 21:16

Взгляните на этот похожий вопрос. Инструмент для открытия того же класса ..

Я думаю, что наиболее важным препятствием является наличие собственного загрузчика классов (загрузка из 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"), в которой не повторяется имя пакета.

meriton 01.02.2013 20:42

Что делать, если класс скомпилирован, например из файла .groovy?

Ondra Žižka 28.06.2013 04:58

@meriton: Или, чтобы выжить refactorinsgs: Test.class.getResource(Test.class.getSimpleName() + ".class")

leonbloy 21.07.2013 20:50

Однако для BouncyCastleProvider требуется полное имя пакета.

Pavel Vlasov 07.11.2013 17:34

getClassLoader() может вернуть null. См. здесь для расширения этого метода, чтобы справиться с этим.

OldCurmudgeon 04.01.2014 14:47

@meriton Class#getResource(String name) делегирует загрузчику классов объекта, то есть методу ClassLoader#getResource(String name).

ahmehri 08.08.2014 17:28

Также getResource может возвращать null. Этот код не будет работать каждый раз.

Tomáš Zato - Reinstate Monica 07.03.2015 21:14

@ TomášZato: Это будет работать, если класс доступен загрузчику классов. Я согласен с тем, что если OP должен справиться с возможностью того, что класс недоступен, им нужно будет выполнить нулевую проверку - хотя это простая модификация.

Jon Skeet 08.03.2015 15:39

Я использовал это для загрузки ресурсов, и здесь это не удалось. В частности, это не удалось, когда я запускал тестовый класс Maven - в этот момент класс в моем проекте пытался загрузить ресурсы из /target/test-classes вместо /target/classes. Проблема была решена с помощью new MyClass().getClass().getResource("...") - или просто this.getClass ..., если в конкретном контексте.

Tomáš Zato - Reinstate Monica 08.03.2015 16:59
getClass().getProtectionDomain().getCodeSource().getLocation();

Да, хотя он не работает с установленным менеджером безопасности и без необходимых разрешений.

Tom Hawtin - tackline 23.10.2008 17:35

К вашему сведению, NPE = исключение нулевого указателя. HTH!

Evgeni Sergeev 19.11.2015 12:40

Этот метод предпочтительнее, если у вас есть ссылка на экземпляр, поскольку вы можете загрузить один и тот же класс из двух разных мест.

Miguel Ping 24.07.2017 13:04

Также не работает при вызове из модуля Java 9+ (о котором, конечно, вы не могли знать в 2008 году).

Jeff G 30.09.2018 23:40

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

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

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

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

это сработало очень хорошо и не имеет проблемы с классами с нулевым ClassLoader

lexicalscope 09.09.2011 15:58

@ries Если не нужно делать это программно, это определенно правильный путь, и он решил мою проблему. Однако OP конкретно спросил, как это сделать программно.

SantiBailors 17.02.2017 19:09

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

Andrew Coates 06.01.2020 18:04

MyClass.class - важная часть - getClass () может возвращать прокси! Затем вы можете получить имя типа MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class и нулевой URL.

jalmasi 13.02.2020 18:33

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

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

Другие вопросы по теме