В Java можно узнать, был ли класс уже загружен? - PullRequest
39 голосов
/ 27 января 2009

Можно ли узнать, был ли загружен класс Java, без попытки его загрузки? Class.forName пытается загрузить класс, но я не хочу этот побочный эффект. Есть ли другой способ?

(Я не хочу переопределять загрузчик классов. Я ищу относительно простой метод.)

Ответы [ 5 ]

40 голосов
/ 27 января 2009

(спасибо Алекси) Этот код:

public class TestLoaded {
     public static void main(String[] args) throws Exception {
          java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[] { String.class });
          m.setAccessible(true);
          ClassLoader cl = ClassLoader.getSystemClassLoader();
          Object test1 = m.invoke(cl, "TestLoaded$ClassToTest");
          System.out.println(test1 != null);
          ClassToTest.reportLoaded();
          Object test2 = m.invoke(cl, "TestLoaded$ClassToTest");
          System.out.println(test2 != null);
     }
     static class ClassToTest {
          static {
               System.out.println("Loading " + ClassToTest.class.getName());
          }
          static void reportLoaded() {
               System.out.println("Loaded");
          }
     }
}

Производит:

false
Loading TestLoaded$ClassToTest
Loaded
true

Обратите внимание, что примеры классов отсутствуют в пакете. Требуется полное двоичное имя .

Пример двоичного имени: "java.security.KeyStore$Builder$FileBuilder$1"

28 голосов
/ 27 января 2009

Вы можете использовать метод findLoadedClass (String) в ClassLoader. Возвращает ноль, если класс не загружен.

6 голосов
/ 27 января 2009

Один из способов сделать это - написать Java-агент, используя API инструментария . Это позволит вам записывать загрузку классов JVM.

public class ClassLoadedAgent implements ClassFileTransformer {

    private static ClassLoadedAgent AGENT = null;

    /** Agent "main" equivalent */
    public static void premain(String agentArguments,
            Instrumentation instrumentation) {
        AGENT = new ClassLoadedAgent();
        for (Class<?> clazz : instrumentation.getAllLoadedClasses()) {
            AGENT.add(clazz);
        }
        instrumentation.addTransformer(AGENT);
    }

    private final Map<ClassLoader, Set<String>> classMap = new WeakHashMap<ClassLoader, Set<String>>();

    private void add(Class<?> clazz) {
        add(clazz.getClassLoader(), clazz.getName());
    }

    private void add(ClassLoader loader, String className) {
        synchronized (classMap) {
            System.out.println("loaded: " + className);
            Set<String> set = classMap.get(loader);
            if (set == null) {
                set = new HashSet<String>();
                classMap.put(loader, set);
            }
            set.add(className);
        }
    }

    private boolean isLoaded(String className, ClassLoader loader) {
        synchronized (classMap) {
            Set<String> set = classMap.get(loader);
            if (set == null) {
                return false;
            }
            return set.contains(className);
        }
    }

    @Override
    public byte[] transform(ClassLoader loader, String className,
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
            byte[] classfileBuffer) throws IllegalClassFormatException {
        add(loader, className);
        return classfileBuffer;
    }

    public static boolean isClassLoaded(String className, ClassLoader loader) {
        if (AGENT == null) {
            throw new IllegalStateException("Agent not initialized");
        }
        if (loader == null || className == null) {
            throw new IllegalArgumentException();
        }
        while (loader != null) {
            if (AGENT.isLoaded(className, loader)) {
                return true;
            }
            loader = loader.getParent();
        }
        return false;
    }

}

META-INF / MANIFEST.MF:

Manifest-Version: 1.0 
Premain-Class: myinstrument.ClassLoadedAgent

Недостатком является то, что вы должны загрузить агент при запуске JVM:

java -javaagent:myagent.jar ....etcetera
5 голосов
/ 27 января 2009

Если вы контролируете источник классов, для которых вас интересует, загружены они или нет (в чем я сомневаюсь, но вы не указали в своем вопросе), то Вы можете зарегистрировать свою нагрузку в статическом инициализаторе.

public class TestLoaded {
    public static boolean loaded = false;
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(loaded);
        ClassToTest.reportLoaded();
        System.out.println(loaded);
    }
    static class ClassToTest {
        static {
            System.out.println("Loading");
            TestLoaded.loaded = true;
        }
        static void reportLoaded() {
            System.out.println("Loaded");
        }
    }
}

Выход:

false
Loading
Loaded
true
1 голос
/ 02 октября 2012

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

Попробовав несколько вещей, упомянутых здесь, мне показалось, что следующее помогло. Я не уверен, работает ли он для всех обстоятельств, он может работать только для классов Java, загруженных из файлов JAR.

InputStream is = getResourceAsStream(name);

Где name - это путь к файлу класса, например com/blah/blah/blah/foo.class.

getResourceAsStream вернул null, когда класс не был загружен в мой загрузчик классов или системный загрузчик классов, и вернул ненулевое значение, когда класс уже был загружен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...