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