簡體   English   中英

類加載器如何在 Apache Tomcat 中部署的 WAR 中工作

[英]How does class loaders work in a WAR deployed in Apache Tomcat

我對以下場景中類加載的工作方式感到有些困惑。 現在這是我對類加載的了解。

根據 Tomcat 文檔 類加載按以下順序發生

  1. JVM 的引導類
  2. Web 應用程序的 WEB-INF/類
  3. 您的 Web 應用程序的 /WEB-INF/lib/*.jar
  4. 系統類加載器類
  5. 常見的類加載器類 --> 這是 tomcat 的 lib 目錄,我的外部 jar 在這里

如果它不是網絡應用程序,則類加載按此順序發生

  1. 引導加載程序

  2. 系統加載器

  3. 應用加載器

現在,就我而言,我有一個 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 類加載。 嘗試從外部 jar 加載時出現ClassNotFoundException的原因可能是什么。 類加載器不應該在WEB-INF/Classes找到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) ~[?:?]

ObjectInputStream#readObject調用ObjectInputStream#resolveClass來檢索對象。 默認實現使用當前線程堆棧上的第一個加載器,因此它在您的情況下使用通用加載器。 類加載器只能找到自己的類及其祖先的類,因此無法在您的 Web 應用程序中找到這些類。

如果要反序列化 Web 應用程序中的對象,則需要使用 Web 應用程序的類加載器,該類加載器在當前線程上設置為上下文類加載器。 因此,您需要像這樣擴展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);
      }
   }
}

編輯:通用類加載器無法從您的應用程序加載類,因為它使用通常的算法:

  1. 詢問父( System )類加載器,
  2. 看看你自己的位置。

另一方面,Web 應用程序類加載器使用以下算法:

  1. 詢問Bootstrap類加載器,
  2. 看看你自己的位置,
  3. 詢問父( Common )類加載器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM