В Java можно узнать, загружен ли класс?
Возможно ли узнать, загружен ли класс Java, не пытаясь его загрузить? Class.forName
пытается загрузить класс, но я не хочу этого побочного эффекта. Есть ли другой способ?
(Я не хочу переопределять загрузчик классов. Я ищу относительно простой метод.)
Ответы
Ответ 1
(Спасибо Алекси) Этот код:
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"
Ответ 2
Вы можете использовать метод findLoadedClass (String) в ClassLoader. Он возвращает null, если класс не загружен.
Ответ 3
Один из способов сделать это - написать агент Java с помощью инструментального API. Это позволит вам записывать загрузку классов с помощью 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
Ответ 4
Если, вы контролируете источник классов, для которых вас интересует, загружены они или нет (что я сомневаюсь, но вы не указываете в своем вопросе) то вы можете зарегистрировать свою нагрузку в статическом инициализаторе.
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
Ответ 5
У меня была аналогичная проблема в последнее время, когда я подозревал, что мои классы загружаются (предположительно через -classpath или что-то подобное) моими пользователями, которые конфликтуют с классами, которые я загружал позже в свой собственный загрузчик классов.
Попробовав несколько из упомянутых здесь вещей, мне показалось, что это трюк. Я не уверен, что он работает для любых обстоятельств, он может работать только для классов java, загруженных из файлов jar.
InputStream is = getResourceAsStream(name);
Где name
- путь к файлу класса, например com/blah/blah/blah/foo.class
.
getResourceAsStream
возвратил null
, когда класс не был загружен в мой загрузчик классов или загрузчик системного класса и возвратил ненулевое значение, когда класс уже был загружен.