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

前端 未结 5 397
不知归路
不知归路 2020-11-27 03:55

Is it possible to know whether a Java class has been loaded, without attempting to load it? Class.forName attempts to load the class, but I don\'t want

相关标签:
5条回答
  • 2020-11-27 04:21

    (Thanks to Aleksi) This code:

    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"

    0 讨论(0)
  • 2020-11-27 04:24

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

    0 讨论(0)
  • 2020-11-27 04:25

    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.

    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.

    InputStream is = getResourceAsStream(name);
    

    Where name is the path to the class file such as 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.

    0 讨论(0)
  • 2020-11-27 04:35

    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
    
    0 讨论(0)
  • 2020-11-27 04:37

    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.

    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
    

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

    java -javaagent:myagent.jar ....etcetera
    
    0 讨论(0)
提交回复
热议问题