Знайдіть, звідки завантажений клас java


184

Хтось знає, як програмно з'ясувати, звідки насправді завантажувач класу java завантажує клас?

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

Тож як я можу змусити навантажувача сказати мені, звідки на диску надходить власне файл класу?

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


4
@JarrodRoberson Я не думаю, що це слід вважати дублікатом stackoverflow.com/questions/11747833/…, оскільки це запитання було задано у 2008 році, і це питання було задано у 2012 році
luke

Відповіді:


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")яка не повторює назву пакета.
meriton

1
Що робити, якщо клас складено, наприклад, з .groovy-файлу?
Ondra Žižka

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
Так, хоча він не працює з встановленим менеджером безпеки та без необхідних дозволів.
Том Хотін - тайклін

1
FYI, NPE = Виключення нульових покажчиків. HTH!
Євгеній Сергєєв

Цей спосіб є кращим, якщо у вас є посилання на екземпляр, оскільки ви можете завантажувати один і той же клас з двох різних місць.
Мігель Пінг

1
Також не працює, коли дзвонить з модуля Java 9+ (що, звичайно, ви не могли знати в 2008 році).
Джефф G

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 () може повернути проксі! Тоді ви можете отримати ім’я на зразок MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class та нульову URL-адресу.
jalmasi


1

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

System.out.println (java.lang.String.class.getResource (String.class.getSimpleName () + ". Class"));

Вихідний приклад:

jar: файл: / D: /Java/jdk1.8/jre/lib/rt.jar! /java/lang/String.class

Або

String obj = "простий тест"; System.out.println (obj.getClass (). GetResource (obj.getClass (). GetSimpleName () + ". Class"));

Вихідний приклад:

jar: файл: / D: /Java/jdk1.8/jre/lib/rt.jar! /java/lang/String.class


0

Цей підхід працює як для файлів, так і для банок:

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.