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
(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"
You can use the findLoadedClass(String) method in ClassLoader. It returns null if the class is not loaded.
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.
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
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