Pergunta

É possível saber se uma classe Java foi carregado, sem tentar carregá-lo? tentativas Class.forName carregar a classe, mas eu não quero esse efeito colateral. Existe outra maneira?

(Eu não quero substituir o carregador de classe. Eu estou procurando um método relativamente simples.)

Foi útil?

Solução

(Graças a Aleksi) Este código:

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

Produz:

false
Loading TestLoaded$ClassToTest
Loaded
true

Note que o exemplo classes não estão em um pacote. A nome binário é necessária.

Um exemplo de um nome binário é "java.security.KeyStore$Builder$FileBuilder$1"

Outras dicas

Você pode usar o findLoadedClass (string) método em ClassLoader. Ele retorna nulo se a classe não está carregado.

Uma maneira de fazer isso seria escrever um agente de Java usando o instrumentação API . Isso permitirá que você para gravar o carregamento de aulas pela 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

A desvantagem é que você tem que carregar o agente quando iniciar o JVM:

java -javaagent:myagent.jar ....etcetera

Se você está no controle da fonte das classes para o qual você está interessado em saber se eles são carregados ou não (o que duvido, mas você não estado na sua pergunta), em seguida, você pode registrar sua carga em um inicializador estático.

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

Output:

false
Loading
Loaded
true

Eu tive um problema semelhante recentemente, onde eu suspeito que as aulas estavam sendo carregados (presumivelmente por meio de -classpath ou algo similar) por meus usuários que entraram em conflito com as classes I estava carregando mais tarde em meu próprio carregador de classe.

Depois de tentar algumas das coisas mencionadas aqui, o seguinte parecia fazer o truque para mim. Eu não tenho certeza se ele funciona para todas as circunstâncias, pode funcionar apenas para classes Java carregados a partir de arquivos jar.

InputStream is = getResourceAsStream(name);

Onde name é o caminho para o arquivo de classe, como com/blah/blah/blah/foo.class.

getResourceAsStream voltou null quando a classe não tinha sido carregado em meu carregador de classe, ou o carregador de classe do sistema, e voltou não nulo quando a classe já havia sido carregado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top