是否可以知道是否已加载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代理.这将允许您记录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> classMap = new WeakHashMap >(); 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 set = classMap.get(loader); if (set == null) { set = new HashSet (); classMap.put(loader, set); } set.add(className); } } private boolean isLoaded(String className, ClassLoader loader) { synchronized (classMap) { Set 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