Java ClassLoader and Context ClassLoader
Java ClassLoader and Context ClassLoader
Class loading can be a difficult subject in Java. Mostly it happens automatically. But when it doesn't and the programmer has to select the correct class loader it's easy to be confused.There are basically four choices.
- Use the system class loader.
- Use the current class loader.
- Use the context class loader.
- Roll your own class loader.
System Class Loader
The first one is easy. The system class loader loads system stuff. If it's not part of the Java API, the system class loader won't load it. This is also the class loader that you configure when set the class path of your application. So the system class loader will find resources that you've set with the class path.Remember that all class loaders by default will eventually invoke their parent class loaders. That means all requests eventually bubble up to the system class loader. Thus you almost always don't want to use the system class loader directly, since it'll be invoked by any other class loader you call. Call the current class loader or the context class loader, and the system class loader will be tried at some point if needed.
Note that with choice number four, roll your own, it is possible to make a class loader that does not call its parent. Some frameworks do this. Read the damn manual (*cough JSP/Servlets cough*). You do have to understand what is loaded and when to use those special packages anyway, so read up. I won't cover any more about those here.
Current Class Loader
The second choice the current class loader. This is the correct choice much of the time. To get the current class loader, call getClass().getClassLoader() on your current class.
public MyClass {
public myMethod() {
...
getClass().getClassLoader();
...
}
}
public myMethod() {
...
getClass().getClassLoader();
...
}
}
If in a static context, just use the class literal (You_Class_Name.class) instead of getClass().
public MyClass {
public static myMethod() {
...
MyClass.class.getClassLoader();
...
}
}
public static myMethod() {
...
MyClass.class.getClassLoader();
...
}
}
This will load resources from within your own Jar file. Wherever your Jar goes, this will follow and always load from resources within that Jar.
What is a resource within a Jar file? I'm glad you asked. A resource is just a file that you've bundled with the Jar file. In other words, if you take your application and add a file to it like this:
myJar.jar
META-INF
MANIFEST.MF
myPackage
Main.class
images
images
Favorites.png
Here, I've made a simple Jar file, with a subdirectory next to the Main.class. Recall that "Main.class" is just what the Java compiler calls your Main.java file after it is compiled. The subdirectory "images" is just made with a regular "mkdir" command (or whatever your OS uses) and then a file (in this case "Favorites.png") is copied in to the subdirectory just like a regular file. The entire group of files can be assembled into a single Jar file with a command:
jar -cvf myJar.jar <input_dir>
The jar command will copy all files in a subdirectory by default. You don't have to list each individual file name. Don't try to use * or */* or **/*, it'll just mess up. Here's the command I used. Note I called "myPackage" "frameimage":
jar -cvfe myJar.jar frameimage.Main -C build/classes frameimage
This command makes "myJar.jar". The extension is required, I could have named it "myJar.zip" or "fred" if I wanted. The entry point (e) is set to "frameimage.Main" which is the fully qualified name of my main class. My IDE normally builds classes in a subdirectory named build/classes so I use the -C switch to pop over there first. Finally, my entire program is packaged in "frameimage" (package names and subdirectory names are the same in Java) so I just name "frameimage" as my single argument for the jar command, and it fetches all files under that subdirectory and puts them in the Jar file. Here's the output (because I used the v option).added manifest
adding: frameimage/(in = 0) (out= 0)(stored 0%)
adding: frameimage/images/(in = 0) (out= 0)(stored 0%)
adding: frameimage/images/Favorites.png(in = 25695) (out= 25629)(deflated 0%)
adding: frameimage/Main$1.class(in = 502) (out= 332)(deflated 33%)
adding: frameimage/Main.class(in = 2077) (out= 1118)(deflated 46%)
adding: frameimage/(in = 0) (out= 0)(stored 0%)
adding: frameimage/images/(in = 0) (out= 0)(stored 0%)
adding: frameimage/images/Favorites.png(in = 25695) (out= 25629)(deflated 0%)
adding: frameimage/Main$1.class(in = 502) (out= 332)(deflated 33%)
adding: frameimage/Main.class(in = 2077) (out= 1118)(deflated 46%)
The additional class "Main$1.class" is just an anonymous inner class that I use to kick off GUI creation.
Hopefully this provides a 100% bomb-proof expaination of where resources go. A lot of folks seem confused, and blame their class loader, when what really happened is they put the resource in the wrong spot.
How does this resource get loaded?
Main.class.getResource( "images/Favorites.png" );
In this case I used a static context, so I used the class literal "Main.class" instead of getClass(). That's it. The name of the resource is just a relative path to the resource itself. The subdirectory "images" is in the same package (subdirectory) "frameimage" as Main, so we just load up resources from there as if we were accessing a relative file name.There's a sample Jar file at the end of this article. Download it and check it out.
Context Class Loader
According to an article on context class loaders at Java World, context class loaders were intended as a back door for the current class loader discussed above. What happens when a resource isn't part of your Jar file, like above, and might in fact come from any place, including places you have no knowledge of or can't control?
That's a context class loader. Context class loaders are attached to threads, not classes. They're set by the caller, which means that the context class loader can be set to any class loader at all, even one completely different from either the current class loader or the system class loader. By default, it is "typically set to the class loader used to load the application" (see the Java Thread documentation).
To me, that implies the system class loader. For an applet, that may mean the current class loader used by your main class. Some experimentation may be required if you are not sure.
The context class loader was intended to be used by frameworks. That is, large libraries which do specialized things in a Java program. Call setContextClassLoader() to set the class loader for a thread, make a library call, and the library will use the correct class loader (instead of its own, which is probably wrong).
Note that any framework or library must call
Thread.currentThread().getContextClassLoader();
To get the context class loader, it won't happen automatically. Thus you need to read the documentation on your library to discover it it uses this feature.Likewise, if a library function uses call backs, it may expect you to uses the context class loader instead of the current class loader. Again, read the documentation carefully to find out.
Create You Own Class Loader
You can easily create your own class loaders by sub-classing:
public class MyClassLoader extends ClassLoader {
....
}
....
}
However this is a pretty advanced topic that I can't cover here. Just know that it exists, and if you do need to know more, Google is your friend.