Java ObjectInputStream#readObject使用外部Jar类引发ClassNotFoundException
因此,我有一个Spring引导应用程序,它从以下路径加载外部JAR:Java ObjectInputStream#readObject使用外部Jar类引发ClassNotFoundException,java,spring,spring-boot,java-8,Java,Spring,Spring Boot,Java 8,因此,我有一个Spring引导应用程序,它从以下路径加载外部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。通过指定 -Dloader.path=… 所有外
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。通过指定
-Dloader.path=…
所有外部jar都依赖于“main-0.0.1-SNAPSHOT.jar”中的接口,它们或多或少都应该进行对象序列化。
该接口称为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();
}
在一些测试之后,我觉得Class.forName()抛出了ClassNotFoundException
,因为默认的类加载器很难找到moe.ofs.addon.navdata.domain.Navaid
,这正是我试图反序列化的类
导航设备实现可序列化
,并且它还有一个静态最终长serialVersionUID
我曾希望通过为当前线程设置上下文类加载器来解决这个问题,以便ObjectInputStream
将使用Spring引导类加载器来解析Navaid
类:
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
当打印出来时,它会给出如下信息
Thread.currentThread().getContextClassLoader() = org.springframework.boot.loader.LaunchedURLClassLoader@7a0b4753
除了ObjectInputStream#readObject
仍然抛出ClassNotFoundException
。
如果我显式调用从Spring引导加载程序加载Navaid类,例如:
getClass().getClassLoader().loadClass("moe.ofs.addon.navdata.domain.Navaid");
它返回一个无任何问题的导航设备实例
正如所料,当直接呼叫时
Class.forName("moe.ofs.addon.navdata.domain.Navaid")
抛出ClassNotFoundException
,即使线程上下文加载程序已显式设置为launchDurlClassLoader
ObjectInputStream#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
ClassLoader cl=getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(cl);
System.out.println(“Thread.currentThread().getContextClassLoader()=”+Thread.currentThread().getContextClassLoader());
类tClass=getClass().getClassLoader().loadClass(“java.io.ObjectInputStream”);
System.out.println(“tClass=“+tClass”);
路径configFilePath=configPath.resolve(文件名+“.data”);
FileInputStream FileInputStream=新的FileInputStream(configFilePath.toFile());
Constructor=tClass.getConstructor(InputStream.class);
ObjectInputStream ObjectInputStream=(ObjectInputStream)构造函数.newInstance(fileInputStream);
objectInputStream.readObject();//抛出ClassNotFoundException
欢迎您的任何意见。提前感谢。据我所知,您应该在ObjectInputStream
诸如此类:
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();
}
default T readFile(字符串文件名、类加载器)抛出IOException、ClassNotFoundException{
路径configFilePath=configPath.resolve(文件名+“.data”);
FileInputStream FileInputStream=新的FileInputStream(configFilePath.toFile());
ObjectInputStream ObjectInputStream=新ObjectInputStream(fileInputStream){
受保护类resolveClass(ObjectStreamClass desc)
抛出IOException,ClassNotFoundException{
试一试{
返回类.forName(desc.getName(),false,loader);
}捕获(ClassNotFoundException cnfe){
返回super.resolveClass(desc);
}
}
};
返回(T)objectInputStream.readObject();
}
我自己从未试过,但值得一试
如果您的项目中有commons io,也会有问题
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
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();
}