在Java中,是否有可能知道一个类是否已经被加载?
是否有可能知道一个Java类是否已经加载, 而不尝试加载它? Class.forName
尝试加载类,但我不希望这种副作用。 有另一种方法吗?
(我不想重写类加载器,我正在寻找一个相对简单的方法。)
(感谢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"); } } }
生产:
false Loading TestLoaded$ClassToTest Loaded true
请注意,示例类不在包中。 完整的二进制名称是必需的。
一个二进制名称的例子是"java.security.KeyStore$Builder$FileBuilder$1"
您可以在ClassLoader中使用findLoadedClass(String)方法。 如果类未加载,则返回null。
一种方法是使用Instrumentation API编写Java代理。 这将允许您logging由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
如果你在控制你感兴趣的类的来源是否加载(我怀疑,但你没有说你的问题),那么你可以注册你的负载在一个静态的初始化。
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
我最近有一个类似的问题,我怀疑类是加载的(大概是通过类path或类似的方式)我的用户冲突与我以后加载到我自己的类加载器的类。
在尝试了几个这里提到的事情之后,下面的这些对我来说似乎是诀窍了。 我不确定它是否适用于任何情况,它可能只适用于从jar文件加载的java类。
InputStream is = getResourceAsStream(name);
其中name
是类文件的path,例如com/blah/blah/blah/foo.class
。
当类没有加载到我的类加载器或系统类加载器中时, getResourceAsStream
返回null
,并且在类已经加载时返回非null。