gpt4 book ai didi

java - 尝试在运行时从外部 JAR 加载类时出现 ClassNotFoundException

转载 作者:行者123 更新时间:2023-11-30 02:38:54 25 4
gpt4 key购买 nike

我正在尝试在运行时从 JAR 中加载一个表示为 byte[] 数组的类。
我知道有关要加载的类的两件事:

1.它实现了“RequiredInterface”
2.我知道它的限定名称:“sample.TestJarLoadingClass”

我找到了solution我必须扩展 ClassLoader 但它会抛出:

Exception in thread "main" java.lang.ClassNotFoundException: sample.TestJarLoadingClass
at java.lang.ClassLoader.findClass(ClassLoader.java:530)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at tasks.Main.main(Main.java:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

每当我想加载类时。

造成这种情况的原因是什么?我该如何摆脱这种情况?
非常感谢任何帮助

主要方法:

public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
Path path = Paths.get("src/main/java/tasks/sample.jar");
RequiredInterface requiredInterface = (RequiredInterface) Class.forName("sample.TestJarLoadingClass", true, new ByteClassLoader(Files.readAllBytes(path))).newInstance();
}

自定义类加载器:

    import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ByteClassLoader extends ClassLoader {
private final byte[] jarBytes;
private final Set<String> names;

public ByteClassLoader(byte[] jarBytes) throws IOException {
this.jarBytes = jarBytes;
this.names = loadNames(jarBytes);
}

private Set<String> loadNames(byte[] jarBytes) throws IOException {
Set<String> set = new HashSet<>();
try (ZipInputStream jis = new ZipInputStream(new ByteArrayInputStream(jarBytes))) {
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
set.add(entry.getName());
}
}
return Collections.unmodifiableSet(set);
}

@Override
public InputStream getResourceAsStream(String resourceName) {
if (!names.contains(resourceName)) {
return null;
}
boolean found = false;
ZipInputStream zipInputStream = null;
try {
zipInputStream = new ZipInputStream(new ByteArrayInputStream(jarBytes));
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
if (entry.getName().equals(resourceName)) {
found = true;
return zipInputStream;
}
}
} catch (IOException e) {;
e.printStackTrace();
} finally {
if (zipInputStream != null && !found) {
try {
zipInputStream.close();
} catch (IOException e) {;
e.printStackTrace();
}
}
}
return null;
}
}

所需接口(interface):

public interface RequiredInterface {
String method();
}

JAR 文件中的类:

package sample;
public class TestJarLoadingClass implements RequiredInterface {
@Override
public String method() {
return "works!";
}
}

最佳答案

我认为我们这里有两个问题:

首先,您应该重写包含加载类的实际逻辑的findClass方法。这里的主要挑战是找到包含您的类的字节数组的一部分 - 因为您将整个 jar 作为字节数组,所以您将需要使用 JarInputStream 来扫描您的类的字节数组。 p>

但这可能还不够,因为您的 RequiredInterface 对于您的 ByteClassLoader 来说是未知的 - 因此您将能够读取类本身,但类的定义包含以下信息:它实现了 RequiredInterface 这对你的类加载器来说是一个问题。这个问题很容易修复,您只需将常规类加载器作为构造函数参数传递给您的类加载器并使用 super(parentClassLoader)

这是我的版本:

public class ByteClassLoader extends ClassLoader {
private final byte[] jarBytes;

public ByteClassLoader(ClassLoader parent, byte[] jarBytes) throws IOException {
super(parent);
this.jarBytes = jarBytes;
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {

// read byte array with JarInputStream
try (JarInputStream jis = new JarInputStream(new ByteArrayInputStream(jarBytes))) {
JarEntry nextJarEntry;

// finding JarEntry will "move" JarInputStream at the begining of entry, so no need to create new input stream
while ((nextJarEntry = jis.getNextJarEntry()) != null) {

if (entryNameEqualsClassName(name, nextJarEntry)) {

// we need to know length of class to know how many bytes we should read
int classSize = (int) nextJarEntry.getSize();

// our buffer for class bytes
byte[] nextClass = new byte[classSize];

// actual reading
jis.read(nextClass, 0, classSize);

// create class from bytes
return defineClass(name, nextClass, 0, classSize, null);
}
}
throw new ClassNotFoundException(String.format("Cannot find %s class", name));
} catch (IOException e) {
throw new ClassNotFoundException("Cannot read from jar input stream", e);
}
}


private boolean entryNameEqualsClassName(String name, JarEntry nextJarEntry) {

// removing .class suffix
String entryName = nextJarEntry.getName().split("\\.")[0];

// "convert" fully qualified name into path
String className = name.replace(".", "/");

return entryName.equals(className);
}
}

及用法

    RequiredInterface requiredInterface = (RequiredInterface)Class.forName("com.sample.TestJarLoadingClass", true, new ByteClassLoader(ByteClassLoader.class.getClassLoader(), Files.readAllBytes(path))).newInstance();
System.out.println(requiredInterface.method());

请注意,我的实现假设文件名 = 类名,因此例如不会找到非顶级的类。当然,一些细节可能会更加完善(例如异常处理)。

关于java - 尝试在运行时从外部 JAR 加载类时出现 ClassNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42325699/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com