gpt4 book ai didi

java - ObjectInputStream#readObject 使用外部 Jar 类引发 ClassNotFoundException

转载 作者:行者123 更新时间:2023-12-02 09:06:17 27 4
gpt4 key购买 nike

所以我有一个 Spring Boot 应用程序,它从以下路径加载外部 jar:

java -cp "main-0.0.1-SNAPSHOT.jar" -Dloader.path="%USERPROFILE%\Addons\" -Dloader.main=moe.ofs.backend.BackendApplication org.springframework.boot.loader.PropertiesLauncher

主 jar 在编译时不知道外部 jar。外部 jar 像“插件”或“附加组件”一样通过指定来加载

-Dloader.path=...

所有外部 jar 都依赖于“main-0.0.1-SNAPSHOT.jar”中的接口(interface),并且它们应该或多或少地执行对象序列化。该接口(interface)称为Configurable,它提供了两个默认方法,如下所示:

default <T extends Serializable> void writeFile(T object, String fileName) throws IOException {
Path configFilePath = configPath.resolve(fileName + ".data");
FileOutputStream fileOutputStream = new FileOutputStream(configFilePath.toFile());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
}

default <T extends Serializable> T readFile(String fileName) throws IOException, ClassNotFoundException {
Path configFilePath = configPath.resolve(fileName + ".data");
FileInputStream fileInputStream = new FileInputStream(configFilePath.toFile());
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
return (T) objectInputStream.readObject();
}

外部 jar 中的类实现此接口(interface),并调用 readFile()writeFile()

writeFile() 工作得很好,似乎不会引起任何问题;然而,readFile() 抛出了 ClassNotFoundException,这就是我想要弄清楚的。

java.lang.ClassNotFoundException: moe.ofs.addon.navdata.domain.Navaid
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:719)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1922)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1805)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2096)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1624)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:464)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at java.util.ArrayList.readObject(ArrayList.java:797)
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 java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1170)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2232)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2123)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1624)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:464)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at moe.ofs.backend.Configurable.lambda$readFile$0(Configurable.java:186)
at java.lang.Thread.run(Thread.java:748)

经过一些测试,在我看来,Class.forName() 抛出了ClassNotFoundException,因为默认的类加载器很难寻找moe.ofs.addon.navdata.domain。 Navaid,这是我试图反序列化的类。

Navaid 实现了 Serialized,并且它还有一个 static Final long serialVersionUID

我希望可以通过为当前线程设置上下文类加载器来解决这个问题,以便 ObjectInputStream 将使用 Spring Boot 类加载器来解析 Navaid 类:

Thread.currentThread().setContextClassLoader(getClass().getClassLoader());

打印出来时,会给出类似的内容

Thread.currentThread().getContextClassLoader() = org.springframework.boot.loader.LaunchedURLClassLoader@7a0b4753

除了ObjectInputStream#readObject仍然抛出ClassNotFoundException。如果我显式调用从 Spring Boot 加载器加载 Navaid 类,例如:

getClass().getClassLoader().loadClass("moe.ofs.addon.navdata.domain.Navaid");

它返回一个 Navaid 实例,没有任何问题。

正如预期的那样,直接调用时

Class.forName("moe.ofs.addon.navdata.domain.Navaid")

即使线程上下文加载器已显式设置为 LaunchedURLClassLoader,也会引发 ClassNotFoundExceptionObjectInputStream#readObject 始终尝试通过调用系统默认类加载器来加载类来解析类。

然后我尝试使用 LaunchedURLClassLoader 加载 ObjectInputStream,但该实例仍然使用系统默认类加载器中的 Class.forName()

ClassLoader cl = getClass().getClassLoader();

Thread.currentThread().setContextClassLoader(cl);

System.out.println("Thread.currentThread().getContextClassLoader() = " + Thread.currentThread().getContextClassLoader());

Class<?> tClass = getClass().getClassLoader().loadClass("java.io.ObjectInputStream");
System.out.println("tClass = " + tClass);

Path configFilePath = configPath.resolve(fileName + ".data");
FileInputStream fileInputStream = new FileInputStream(configFilePath.toFile());

Constructor<?> constructor = tClass.getConstructor(InputStream.class);

ObjectInputStream objectInputStream = (ObjectInputStream) constructor.newInstance(fileInputStream);

objectInputStream.readObject(); // throws ClassNotFoundException

欢迎任何意见。提前致谢。

最佳答案

据我所知,您应该重写ObjectInputStream上的方法resolveClass

类似的事情:

default <T extends Serializable> T readFile(String fileName, ClassLoader loader) throws IOException, ClassNotFoundException {
Path configFilePath = configPath.resolve(fileName + ".data");
FileInputStream fileInputStream = new FileInputStream(configFilePath.toFile());
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream){
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
try {
return Class.forName(desc.getName(), false, loader);
} catch(ClassNotFoundException cnfe) {
return super.resolveClass(desc);
}
}
};
return (T) objectInputStream.readObject();
}

我自己从未尝试过,但值得一试。

还有http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/input/ClassLoaderObjectInputStream.html如果您的项目中已有 commons-io。

https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/input/ClassLoaderObjectInputStream.java

关于java - ObjectInputStream#readObject 使用外部 Jar 类引发 ClassNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61047670/

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