是否可以知道是否已加载Java类,没有尝试加载它? Class.forName
尝试加载该类,但我不想要这种副作用。还有另一种方式吗?
(我不想覆盖类加载器。我正在寻找一种相对简单的方法。)
答案 0 :(得分:40)
(感谢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
请注意,示例类不在包中。完整binary name是必需的。
二进制名称的示例是"java.security.KeyStore$Builder$FileBuilder$1"
答案 1 :(得分:28)
您可以在ClassLoader中使用findLoadedClass(String)方法。如果未加载类,则返回null。
答案 2 :(得分:6)
执行此操作的一种方法是使用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<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
答案 3 :(得分:5)
如果你控制了你感兴趣的课程的来源是否被加载(我怀疑,但你没有在你的问题中说明),然后你可以在静态初始化器中注册你的负载。
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
答案 4 :(得分:1)
我最近遇到了类似的问题,我怀疑我的用户正在加载类(可能是通过-classpath或类似方式),这些类与我稍后加载到我自己的类加载器中的类冲突。
在尝试了这里提到的一些事情后,以下似乎对我有用。我不确定它是否适用于所有情况,它可能只适用于从jar文件加载的java类。
InputStream is = getResourceAsStream(name);
其中name
是类文件的路径,例如com/blah/blah/blah/foo.class
。
getResourceAsStream
返回null
,并且在已经加载类时返回非null。