ClassLoader
.
(VMSpec 5.3)
A classloader may create a class either by delegation or by defining it directly.
The classloader that initiates loading of a class is known as the initiating loader.
The classloader that defines the class is known as the defining loader.
In practical terms: understanding and appreciating this distinction is crucial when debugging issues
concerning classloaders.
ClassLoader
supplied by the Java Virtual Machine.
All others are user (also known as application) ClassLoader
instances.
In practical development terms: The System classloader returned by Classloader.getSystemClassLoader()
will be either the bootstrap classloader or a direct descendent of the bootstrap classloader.
Only when debugging issues concerning the system classloader should there be any need to consider the detailed
differences between the bootstrap classloader and the system classloader.
ClassLoader
class insists that every implementation has a parent class loader.
This delegation model therefore naturally forms a tree structure rooted in the bootstrap classloader.
Containers (i.e. applications such as servlet engines or application servers
that manage and provide support services for a number of "contained" applications
that run inside of them) often use complex trees to allow isolation of different applications
running within the container. This is particularly true of J2EE containers.
Child-first classloading has the advantage of helping to improve isolation between containers and the applications inside them. If an application uses a library jar that is also used by the container, but the version of the jar used by the two is different, child-first classloading allows the contained application to load its version of the jar without affecting the container.
The ability for a servlet container to offer child-first classloading is made available, as an option, by language in the servlet spec (Section 9.7.2) that allows a container to offer child-first loading with certain restrictions, such as not allowing replacement of java.* or javax.* classes, or the container's implementation classes.
Though child-first and parent-first are not the only strategies possible, they are by far the most common. All other strategies are rare. However, it is not uncommon to be faced with a mixture of parent-first and child-first classloaders within the same hierarchy.
The class loader used to define a class is available programmatically by calling
the getClassLoader
method
on the class in question. This is often known as the class classloader.
Java 1.2 introduces a mechanism which allows code to access classloaders
which are not the class classloader or one of its parents.
A thread may have a class loader associated with it by its creator for use
by code running in the thread when loading resources and classes.
This classloader is accessed by the getContextClassLoader
method on Thread
. It is therefore often known as the context classloader.
Note that the quality and appropriateness of the context classloader depends on the care with which the thread's owner manages it.
The Javadoc for
Thread.setContextClassLoader
emphasizes the setting of the
context classloader as an aspect of thread creation. However, in many
applications the context classloader is not fixed at thread creation but
rather is changed throughout the life of a thread as thread execution moves
from one context to another. This usage of the context classloader is
particularly important in container applications.
For example, in a hypothetical servlet container, a pool of threads is created to handle HTTP requests. When created these threads have their context classloader set to a classloader that loads container classes. After the thread is assigned to handle a request, container code parses the request and then determines which of the deployed web applications should handle it. Only when the container is about to call code associated with a particular web application (i.e. is about to cross an "application boundary") is the context classloader set to the classloader used to load the web app's classes. When the web application finishes handling the request and the call returns, the context classloader is set back to the container classloader.
In a properly managed container, changes in the context classloader are
made when code execution crosses an application boundary. When contained
application A
is handling a request, the context classloader
should be the one used to load A
's resources. When application
B
is handling a request, the context classloader should be
B
's.
While a contained application is handling a request, it is not unusual for it to call system or library code loaded by the container. For example, a contained application may wish to call a utility function provided by a shared library. This kind of call is considered to be within the "application boundary", so the context classloader remains the contained application's classloader. If the system or library code needs to load classes or other resources only visible to the contained application's classloader, it can use the context classloader to access these resources.
If the context classloader is properly managed, system and library code that can be accessed by multiple applications can not only use it to load application-specific resources, but also can use it to detect which application is making a call and thereby provided services tailored to the caller.
In practice, context classloaders vary in quality and issues sometimes arise when using them. The owner of the thread is responsible for setting the classloader. If the context classloader is not set then it will default to the system classloader. Any container doing so will cause difficulties for any code using the context classloader.
The owner is also at liberty to set the classloader as they wish. Containers may set the context classloader so that it is neither a child nor a parent of the classloader that defines the class using that loader. Again, this will cause difficulties.
Introduced in Java J2EE 1.3 is a requirement for vendors to appropriately set the context classloader. Section 6.2.4.8 (1.4 text):
This specification requires that J2EE containers provide a per thread context class loader for the use of system or library classes in dynamicly loading classes provided by the application. The EJB specification requires that all EJB client containers provide a per thread context class loader for dynamicly loading system value classes. The per thread context class loader is accessed using the Thread method getContextClassLoader. The classes used by an application will typically be loaded by a hierarchy of class loaders. There may be a top level application class loader, an extension class loader, and so on, down to a system class loader. The top level application class loader delegates to the lower class loaders as needed. Classes loaded by lower class loaders, such as portable EJB system value classes, need to be able to discover the top level application class loader used to dynamicly load application classes. We require that containers provide a per thread context class loader that can be used to load top level application classes as described above.
This specification leaves quite a lot of freedom for vendors. (As well as using unconventional terminology and containing the odd typo.) It is a difficult passage (to say the least).
Reflection cannot bypass restrictions imposed by the java language security model, but, by avoiding symbolic
references, reflection can be used to load classes which could not otherwise be loaded. Another ClassLoader
can be used to load a class and then reflection used to create an instance.
Recall that the runtime packaging is used to determine accessibility. Reflection cannot be used to avoid basic java security. Therefore, the runtime packaging becomes an issue when attempting to cast classes created by reflection using other class loaders. When using this strategy, various modes of failure are possible when common class references are defined by the different class loaders.
Reflection is often used with the context classloader. In theory, this allows a class defined in a parent classloader to load any class that is loadable by the application. In practice, this only works well when the context classloader is set carefully.
LogFactory
requires symbolic references to particular Log
implementations. Reflection can be used to load these from an appropriate classloader
without unacceptable performance degradation.
This is the strategy adopted by JCL.
JCL uses the context classloader to load the Log
implementation.