vendredi 12 juillet 2013

Java : Why you should not use getClass().getClassLoader()


ClassLoader is a useful class when dealing with dynamic resource loading.

Common use is to load at runtime some configuration files or others kind of ressource : .properties, .xml, .png, ....

It gives you a convenient access to the classpath of your application.

The classpath ? Is there only one classpath ?

Not so fast !

Classloading


Classloading is one of the most unknown aspect of Java features but it's one of the most important and most magic feature.

Indeed this process is able  to load your code dynamically even if this code is somewhere on the network.

Classloading stands on ClassLoader and there are lot of things done behind the curtain when you start a Java program.

Run a Java application, anything you want, but just add the  -verbose:class option to the Java command line.


Here is a sample output :


[Opened /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Cloneable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassLoader from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.System from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Throwable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Error from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ThreadDeath from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Exception from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.RuntimeException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.AccessControlContext from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ReflectiveOperationException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassNotFoundException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.LinkageError from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.NoClassDefFoundError from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassCastException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ArrayStoreException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.VirtualMachineError from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
...


ClassLoader


It's very important to understand there are many ClassLoader used in a Java Application.

Each ClassLoader is responsible for loading a piece of the overall class path.

At runtime, ClassLoader are linked to each other : this is known as the class loader hierarchy.

Application server use their own hierarchy which can be quite complex.

The key point is : the class loader responsible for loading a class will associated to this class for life, even if this class object is used in a context with another class loader.


getClass().getClassLoader() returns always the same ClassLoader object.


Here is a sample code.

As you can see the parent class loader of my main class can not find a resource which is in my application class path.


Thread context


Each thread has a context class loader.

When  you need to get some resource use the context thread class loader because it has access to the most complete class path.

Indeed, if you use the class loader of any class object, you'll get the class loader which loads this class the very first time.

This class loader may access to your resource but you can not know for sure.

So : Thread.currentThread().getContextClassLoader() is the key !





  










1 commentaire: