在下面的场景中,我对类加载的工作方式有点困惑。下面是我对类加载的了解。
根据tomcat文档。类加载按以下顺序进行
jvm的引导类
web应用程序的web inf/类
/web应用程序的web inf/lib/*.jar
系统类加载器类
公共类装入器类-->这是tomcat的lib dir,我的外部jar在这里
如果它不是一个web应用程序,则类加载按以下顺序进行
引导加载程序
系统加载器
程序加载器
现在,在我的例子中,我有一个web应用程序,它使用外部jar读取一些序列化数据。当尝试读取序列化数据时,jar抛出 ClassNotFoundException
. 但是如果我使用自己的序列化逻辑而不使用jar,那么应用程序就可以工作了。
这是一个错误的例子
public GenericResult getGenericResult( ){
GenericResult cachedResult = externalJar.get( "myKey" ); // This jar uses deserialization
return cachedResult;
}
这是一个有效的例子
public GenericResult getGenericResult( ){
// do not mind resource closing and all as this is a just to show how I did it
FileInputStream fileIn = new FileInputStream(filepath);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
GenericResult cachedResult = (GenericResult)objectIn.readObject();
return cachedResult;
}
外部jar是一个驻留在tomcats lib目录中的jar。我想澄清的是,当从这个外部jar加载一个类时,它是使用基于web应用程序的类加载(如我所指示的第一个)还是使用java类加载(如我所指示的第二个)。那么,得到一份工作的原因是什么呢 ClassNotFoundException
尝试从外部jar加载时。难道不是吗 GenericResult
类装入器无法在中找到类 WEB-INF/Classes
??
例外情况:
java.lang.ClassNotFoundException: com.example.result.GenericResult
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:435) ~[?:?]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589) ~[?:?]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[?:?]
at java.base/java.lang.Class.forName0(Native Method) ~[?:?]
at java.base/java.lang.Class.forName(Class.java:468) ~[?:?]
at java.base/java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:782) ~[?:?]
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2028) ~[?:?]
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1895) ~[?:?]
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2202) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:519) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:477) ~[?:?]
at java.base/java.util.ArrayList.readObject(ArrayList.java:899) ~[?:?]
at java.base/jdk.internal.reflect.GeneratedMethodAccessor1272.invoke(Unknown Source) ~[?:?]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1226) ~[?:?]
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2401) ~[?:?]
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712) ~[?:?]
at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2540) ~[?:?]
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2434) ~[?:?]
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712) ~[?:?]
at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2540) ~[?:?]
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2434) ~[?:?]
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:519) ~[?:?]
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:477) ~[?:?]
1条答案
按热度按时间uqdfh47h1#
ObjectInputStream#readObject
电话ObjectInputStream#resolveClass
检索对象。默认实现使用当前线程堆栈上的第一个加载程序,因此在您的案例中使用公共加载程序。类加载器只能找到自己的类和它的祖先的类,因此它无法在web应用程序中找到这些类。如果要反序列化web应用程序中的对象,则需要使用web应用程序的类加载器,该类加载器在当前线程上设置为上下文类加载器。因此,您需要扩展
ObjectInputStream
这样地:编辑:common classloader无法从您的应用程序加载类,因为它使用通常的算法:
询问父(系统)类加载器,
看看你自己的位置。
另一方面,web应用程序类加载器使用以下算法:
询问引导类加载器,
看看你自己的位置,
询问父(公共)类加载器。