简体   繁体   English

类加载器如何在 Apache Tomcat 中部署的 WAR 中工作

[英]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类加载按以下顺序发生

  1. Bootstrap classes of your JVM JVM 的引导类
  2. WEB-INF/classes of your web application Web 应用程序的 WEB-INF/类
  3. /WEB-INF/lib/*.jar of your web application您的 Web 应用程序的 /WEB-INF/lib/*.jar
  4. System class loader classes系统类加载器类
  5. Common class loader classes --> This is tomcat's lib dir and my external jar is here常见的类加载器类 --> 这是 tomcat 的 lib 目录,我的外部 jar 在这里

And if it is not a web-app, then the class loading happens in this order如果它不是网络应用程序,则类加载按此顺序发生

  1. Bootstrap Loader引导加载程序

  2. System Loader系统加载器

  3. 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:编辑:通用类加载器无法从您的应用程序加载类,因为它使用通常的算法:

  1. Ask the parent ( System ) classloader,询问父( System )类加载器,
  2. Look in your own locations.看看你自己的位置。

On the other hand a web application classloader uses the algorithm:另一方面,Web 应用程序类加载器使用以下算法:

  1. Ask the Bootstrap classloader,询问Bootstrap类加载器,
  2. Look in your own locations,看看你自己的位置,
  3. Ask the parent ( Common ) classloader.询问父( Common )类加载器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ServiceLoader.load 如何与 JPMS 中的 class 加载程序一起使用? - How does ServiceLoader.load work with class loaders in JPMS? 如何在 Tomcat 上部署适当的“覆盖”战争 - How to have a proper "overlay" war deployed on Tomcat 部署特定的.war文件后,Apache Tomcat将无法完成启动 - Apache Tomcat will not complete startup after a certain .war File is deployed apache-tomcat 7.0不解压.war? - apache-tomcat 7.0 does not unpack .war? 部署不同应用程序的多次war时,tomcat容器如何区分要读取的war文件的web.xml - how does tomcat container differentiate which web.xml of war file to read when multiple war of different application is deployed 找不到针对部署到heroku tomcat的Java Web WAR的添加依赖项的类 - Class for added dependency not found for Java Web WAR deployed to heroku tomcat 在 Spring 中增加 class 加载计数 在 Tomcat 上部署为 WAR 的引导应用程序 - Increasing class loaded count in Spring Boot application deployed as WAR on Tomcat Tomcat 战争已部署,但 spring 引导应用程序未开始运行 - Tomcat war gets deployed but spring boot app does not start running Tomcat 7中的已部署战争在Linux上的行为与Windows上的行为不同 - Deployed war in Tomcat 7 behaves differently on Linux than it does on Windows Spring Boot War文件不适用于tomcat - Spring boot war file does not work on tomcat
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM