在 Java 中,是否可以知道类是否已经加载?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/482633/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:13:43  来源:igfitidea点击:

In Java, is it possible to know whether a class has already been loaded?

javaclassloader

提问by Hosam Aly

Is it possible to know whether a Java class has been loaded, withoutattempting to load it? Class.forNameattempts to load the class, but I don't want this side effect. Is there another way?

是否可以在尝试加载的情况下知道 Java 类是否已加载?Class.forName尝试加载类,但我不想要这种副作用。还有其他方法吗?

(I don't want to override the class loader. I'm looking for a relatively simple method.)

(我不想覆盖类加载器。我正在寻找一种相对简单的方法。)

采纳答案by Stephen Denne

(Thanks to Aleksi) This code:

(感谢 Aleksi)这段代码:

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

Produces:

产生:

false
Loading TestLoaded$ClassToTest
Loaded
true
false
Loading TestLoaded$ClassToTest
Loaded
true

Note that the example classes are not in a package. The full binary nameis required.

请注意,示例类不在包中。需要完整的二进制名称

An example of a binary name is "java.security.KeyStore$Builder$FileBuilder$1"

二进制名称的一个例子是 "java.security.KeyStore$Builder$FileBuilder$1"

回答by Aleksi Yrttiaho

You can use the findLoadedClass(String)method in ClassLoader. It returns null if the class is not loaded.

您可以在 ClassLoader 中使用findLoadedClass(String)方法。如果类未加载,则返回 null。

回答by Stephen Denne

Ifyou're in control of the source of the classes for which you are interested in whether they are loaded or not (which I doubt, but you don't state in your question), then you could register your load in a static initializer.

如果您控制着您感兴趣的类的源是否已加载(我对此表示怀疑,但您没有在问题中说明),那么您可以在静态初始化程序中注册您的负载.

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
false
Loading
Loaded
true

回答by McDowell

One way to do this would be to write a Java agent using the instrumentation API. This would allow you to record the loading of classes by the JVM.

一种方法是使用检测 API编写 Java 代理。这将允许您记录 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:

META-INF/MANIFEST.MF:

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

The downside is that you have to load the agent when you start the JVM:

缺点是你必须在启动 JVM 时加载代理:

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

回答by Eric Van Bezooijen

I had a similar problem recently, where I suspected that classes were being loaded (presumably by way of -classpath or something similar) by my users that conflicted with classes I was loading later on into my own classloader.

我最近遇到了类似的问题,我怀疑我的用户正在加载类(大概是通过 -classpath 或类似的方式),这些类与我稍后加载到我自己的类加载器中的类相冲突。

After trying a few of the things mentioned here, the following seemed to do the trick for me. I'm not sure if it works for every circumstance, it may only work for java classes loaded from jar files.

在尝试了这里提到的一些事情之后,以下内容似乎对我有用。我不确定它是否适用于所有情况,它可能只适用于从 jar 文件加载的 java 类。

InputStream is = getResourceAsStream(name);

Where nameis the path to the class file such as com/blah/blah/blah/foo.class.

name类文件的路径在哪里,例如com/blah/blah/blah/foo.class.

getResourceAsStreamreturned nullwhen the class had not been loaded into my class loader, or the system class loader, and returned non-null when the class had already been loaded.

getResourceAsStream回来null上课的时候没有被加载到我的类加载器或系统类加载器,当已经加载的类返回非空。