[英]How does class loaders work in a WAR deployed in Apache Tomcat
I am a bit confused with how the class loading works in the below scenario.我对以下场景中类加载的工作方式感到有些困惑。 Now here's what I know about class loading.现在这是我对类加载的了解。
As per the Tomcat docs .根据 Tomcat 文档。 The class loading happens in the following order类加载按以下顺序发生
And if it is not a web-app, then the class loading happens in this order如果它不是网络应用程序,则类加载按此顺序发生
Bootstrap Loader引导加载程序
System Loader系统加载器
Application Loader应用加载器
Now, in my case, I have a web application that reads some serialized data by using an external jar.现在,就我而言,我有一个 Web 应用程序,它使用外部 jar 读取一些序列化数据。 When trying to read the serialized data, the jar throws ClassNotFoundException
.尝试读取序列化数据时,jar 会抛出ClassNotFoundException
。 But if I use my own serialization logic without using the jar, then the application works.但是如果我在不使用 jar 的情况下使用我自己的序列化逻辑,那么应用程序就可以工作。
This is the erroneous example这是错误的例子
public GenericResult getGenericResult( ){
GenericResult cachedResult = externalJar.get( "myKey" ); // This jar uses deserialization
return cachedResult;
}
This is the working example这是工作示例
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;
}
The external jar is a provided jar residing in the tomcats lib directory.外部 jar 是驻留在 tomcats lib 目录中的提供的 jar。 What I want to clarify is that, when loading a class from this external jar, is it using the web-application based class loading as I indicated first or java class loading as I indicate second.我想澄清的是,当从这个外部 jar 加载一个类时,它是使用我第一个指出的基于 web 应用程序的类加载还是我第二个指出的 java 类加载。 And what could be the reason for getting a ClassNotFoundException
when trying to load from external jar.尝试从外部 jar 加载时出现ClassNotFoundException
的原因可能是什么。 Shouldn't the GenericResult
class be found by the class loader at WEB-INF/Classes
??类加载器不应该在WEB-INF/Classes
找到GenericResult
WEB-INF/Classes
吗?
Exception:例外:
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) ~[?:?]
ObjectInputStream#readObject
calls ObjectInputStream#resolveClass
to retrieve objects. ObjectInputStream#readObject
调用ObjectInputStream#resolveClass
来检索对象。 The default implementation uses the first loader on the current thread's stack, therefore it uses the common loader in your case.默认实现使用当前线程堆栈上的第一个加载器,因此它在您的情况下使用通用加载器。 A classloader can find only his own classes and those of its ancestors, so it can not find the classes in your web application.类加载器只能找到自己的类及其祖先的类,因此无法在您的 Web 应用程序中找到这些类。
If you want to deserialize an object in your web application, you need to use the web application's class loader, which is set as context class loader on the current thread.如果要反序列化 Web 应用程序中的对象,则需要使用 Web 应用程序的类加载器,该类加载器在当前线程上设置为上下文类加载器。 Therefore you need to extend ObjectInputStream
like this:因此,您需要像这样扩展ObjectInputStream
:
public class ClassloaderAwareObjectInputStream extends ObjectInputStream {
public ClassloaderAwareObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class< ? > resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
final String name = desc.getName();
try {
return Class.forName(name, false, tccl);
} catch (ClassNotFoundException ex) {
return super.resolveClass(desc);
}
}
}
Edit: The Common classloader can not load a class from your application, since it uses the usual algorithm:编辑:通用类加载器无法从您的应用程序加载类,因为它使用通常的算法:
On the other hand a web application classloader uses the algorithm:另一方面,Web 应用程序类加载器使用以下算法:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.