简体   繁体   English

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

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

Is it possible to know whether a Java class has been loaded, without attempting to load it? 是否可以尝试加载就知道是否已加载Java类? Class.forName attempts to load the class, but I don't want this side effect. Class.forName尝试加载该类,但我不希望出现这种副作用。 Is there another way? 还有另一种方法吗?

(I don't want to override the class loader. I'm looking for a relatively simple method.) (我不想覆盖类加载器。我正在寻找一个相对简单的方法。)

(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 

Note that the example classes are not in a package. 请注意,示例类不在软件包中。 The full binary name is required. 完整的二进制名称是必需的。

An example of a binary name is "java.security.KeyStore$Builder$FileBuilder$1" 二进制名称的示例为"java.security.KeyStore$Builder$FileBuilder$1"

You can use the findLoadedClass(String) method in ClassLoader. 您可以在ClassLoader中使用findLoadedClass(String)方法。 It returns null if the class is not loaded. 如果未加载该类,则返回null。

One way to do this would be to write a Java agent using the instrumentation API . 一种实现方法是使用工具API编写Java代理。 This would allow you to record the loading of classes by the JVM. 这将允许您记录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

If you'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 

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 name is the path to the class file such as com/blah/blah/blah/foo.class . 其中name是类文件的路径,例如com/blah/blah/blah/foo.class

getResourceAsStream returned null when 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 ,而在类已加载时返回非null。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 Java 中,是否可以知道在使用引导加载程序时是否已经加载了 class? - In Java, is it possible to know whether a class has already been loaded when using the bootstrap loader? 如何验证Java中的ClassLoader是否已加载.jar或类 - How to verify that a .jar or a class has been already loaded by the ClassLoader in Java 如何知道一个类是否已经初始化? - How to know whether a class has been initialized or not? 检查是否已经使用System.load(...)加载了库 - check whether a library has already been loaded with System.load(…) 是否有帮助程序可以知道 Hibernate 是否已加载属性? - Is there a helper to know whether a property has been loaded by Hibernate? Java有没有办法加载一个已被System ClassLoader加载的类不同的类? - Java is there a way to load a class that has been already loaded by the System ClassLoader by a different one? 有没有办法判断一个类是否已加载到jvm? - Is there any way to judge whether a class has been loaded into jvm? 知道文件是否已下载的代码 - code to know whether the file has been downloaded or not 如何判断列表是否已经加载? - How to tell if a list has already been loaded? Java:尚未加载屏幕 - Java: Screen has not been loaded
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM