简体   繁体   中英

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 . The class loading happens in the following order

  1. Bootstrap classes of your JVM
  2. WEB-INF/classes of your web application
  3. /WEB-INF/lib/*.jar of your web application
  4. System class loader classes
  5. Common class loader classes --> This is tomcat's lib dir and my external jar is here

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. When trying to read the serialized data, the jar throws ClassNotFoundException . But if I use my own serialization logic without using the jar, then the application works.

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. 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. And what could be the reason for getting a ClassNotFoundException when trying to load from external jar. Shouldn't the GenericResult class be found by the class loader at 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. 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.

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. Therefore you need to extend ObjectInputStream like this:

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,
  2. Look in your own locations.

On the other hand a web application classloader uses the algorithm:

  1. Ask the Bootstrap classloader,
  2. Look in your own locations,
  3. Ask the parent ( Common ) classloader.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM