Understanding Java Class Loader
Typically class loaders
are arranged in a parent/child hierarchy.
When a class loading
request is presented to a class loader, it first asks its parent class loader
to fulfill the request.
The parent, in turn, asks
its parent for the class until the request reaches the top of the hierarchy.
If the class loader at
the top of the hierarchy cannot fulfill the request, then the child class
loader that called it is responsible for loading the class.
If it can't load the
class, a ClassNotFoundException is thrown by the class loader.
Delegation
Model
Class loaders are
arranged hierarchically in a tree, with the bootstrap class loader as the root
of the tree.
Namespaces
A loaded class in a JVM
is identified by its fully qualified name and its defining class loader, so
class A defined by class loader X is not same class as class A defined by class
loader B. In Eclipse, each plugin has its class loader.
Java default class loaders
Bootstrap
class loader
Bootstrap classloader is
the parent of all classloaders and loads the standard JDK classes in lib
directory of JRE (rt.jar and i18n.jar). All the java.* classes are loaded by
this classloader.
Extensions
class loader (ExtClassLoader)
Extensions Classloader is
the immediate child of Bootstrap classloader. This classloader loads the
classes in lib\ext directory of the JRE.
System class loader
(AppClassLoader)
It loads the classes and
jars specified by the CLASSPATH environment variable, java.class.path system
property, -cp or –classpath command line settings. If any of the jars specified
in one of the above manner have a MANIFEST.MF file with a Class-Path attribute,
the jars specified by the Class-Path attribute are also loaded.
Bootstrap class loader is
pretty special, in that it is implemented in native code. All other class
loaders are written in Java (apart from some native methods) and extend the
java.lang.ClassLoader class.
A class is identified
uniquely in the context of the associated classloader.
Two objects loaded by
different classloaders are never equal.
Three principles of Classloader operation
Delegation
Principle
If a class is not loaded
already, the classloaders delegate the request to load that class to their
parent classloaders. This delegation continues until the top of the hierarchy
is reached and the primordial classloader tries to load the class.
Visibility
Principle
Classes loaded by parent
classloaders are visible to child classloaders but not vice versa, sibling
classloaders cannot see each other’s classes. Requests can only go to a parent
class loader; they cannot go to a child class loader.
Uniqueness
Principle
When a classloader loads
a class, the child classloaders in the hierarchy will never reload that class.
In most implementations
getClassLoader() method returns null for the bootstrap class loader.
J2EE
classloader hierarchy and its implications
In J2EE, each application
is packaged as an Enterprise ARchive (EAR). The EAR is a self-contained
deployment unit having minimal dependencies on external classes (with the
exception of application server classes).
An EAR file is composed
of any number of following components: EJB-JAR, WAR, RAR, Dependency JAR,
application.xml.
J2EE
classloader hierarchy
When the parent
classloader is below the System-Classpath Classloader in hierarchy, the child
classloader can "see" the class only when it is specified in the
manifest file for the child classloader. For instance, if an EJB application
wants to reference util-a.jar and util-b.jar, then we can add the following
entry into the EJB-JAR’s manifest file, MANIFEST.MF: Class-Path: util-a.jar
util-b.jar
Every J2EE EAR
gets its own classloader. This is the starting point for isolating
application classes from one another. This classloader loads all the dependency
libraries in the EAR, both EJB and the web module can see the class loaded by
the EAR classloader.
An interesting question
arises. Aren’t EJBs packaged as jars? How does the application server know to
load the EJBs in a separate classloader? The simple answer is that it doesn’t,
unless you tell it so. You have to explicitly specify which of the jars are EJB
modules in application.xml – the deployment descriptor for the EAR.
WAR
classloaders are the children of EJB classloader. The key difference
between these classloaders is that while all EJB modules whether in the single
or different EJB-JARs, share the same EJB classloader, each WAR gets its own
classloader. All of the WAR classloaders however inherit from the same parent -
EJB classloader. The rationale behind this hierarchy is that EJBs contain the
core of the business logic and web applications have to "see" them to
invoke their business methods. Of course to "see" them the WAR
manifest file has to have an entry as shown earlier.
WebLogic class
loader
One EJB class loader is
created as a child of the system class loaders. It is responsible for loading
all EJB .jar classes for all EJB .jar files in the .ear.
One web application class
loader is created for each .war in the .ear, and each of the web application
class loaders is a child of the EJB class loader. The web application class
loaders are responsible for loading the classes and jars in the WEB-INF/classes
and WEB-INF/lib directories in the corresponding .war.
.jar files listed in a
Manifest Class-Path entry are loaded by the application's EJB class loader.
This applies to Manifest Class-Path entries found in EJB .jar files and web
application .war files within the .ear.
One advantage of this
class loading architecture is the automatic availability of all EJB classes
from the web application class loaders. However, classes found in the WEB-INF/classes
or WEB-INF/lib directories of a .war are not available to any EJB classes.
A WebSphere
extensions class loader
The WebSphere extensions
class loader loads the WebSphere Application Server classes that are required
at run time. The extensions class loader uses a ws.ext.dirs system property to
determine the path that is used to load classes. Each directory in the
ws.ext.dirs class path and every Java archive (JAR) file or ZIP file in these
directories is added to the class path used by this class loader.
One or more
application module class loaders that load elements of enterprise applications
running in the server
The application elements
can be Web modules, enterprise bean (EJB) modules, resource adapter archives
(RAR files), and dependency JAR files. Application class loaders follow Java EE
class-loading rules to load classes and JAR files from an enterprise
application. The product enables you to associate shared libraries with an
application.
Zero or more
Web module class loaders
By default, Web module
class loaders load the contents of the WEB-INF/classes and WEB-INF/lib
directories. Web module class loaders are children of application class
loaders. You can specify that an application class loader load the contents of
a Web module rather than the Web module class loader.
Java class
loader
This kind of tree like
class loader supports delegation, visibility and uniqueness principle
inherently.
Conflicting
classes
You have a program,
running on JRE 1.6, it needs latest and greatest xerces 2.9 explicitly. You
have bundled the xerces jar with your program, and its included in the
classpath. But during runtime, older version of xerces classes are getting loaded
irrespective of your settings. You guessed it right! Its because JRE 1.6 ships
xerces and it is already loaded by extension class loader. This is the primary
problem of hierarchical class loader, at runtime all jars become a long list of
files which is searched in a sequential order in the order of their loading.
Explicit
dependencies can not be defined
Let’s say your program
needs Apache commons-logging, xerces and a long list of popular open source
components. When you ship your product, either you have to build the stack
yourself and hand it over to the customer or communicate this through a user
documentation. The so called logical unit of a java program, the jar, lacks the
place holder in its meta-data to include information about its dependency.
Version
dependency problem
Problem gets complicated
when you explicitly need commons-logging 1.1.1, xerces 2.9 and so on, and your
program is supposed to be part of a larger system which already has these
libraries and their versions are not same as yours. You will have sleep less
night in recompiling your program with those libraries to co-exist. Phew !! I
have been through this many times.
Entire jar is
exposed
When you are building
java libraries you want the user to use only few classes which are meant be
used. But today Java doesn't have any mechanism to define access specifiers at
a class level. Once the jar is in the classpath all core classes can be seen
and instantiated.
J2EE class
loader
Class loader remains
hierarchical but with support for isolation across multiple applications in the
same container.
Application A, lets say
an ear, has its own class loader and has loaded war and sar inside it. Another
Application B, another ear, has its own class loader and can not see
Application A's classes. Now if you have a need to share classes between these
two applications, either you have to make them use the same class loader or
push the shared classes up in the hierarchy to system class loader. By doing
this the entire container will be able to see those shared classes even though
other applications doesn't necessarily need them. JBoss has introduced unified
class loader to address the same problem. But you have to be a pro to use that!
Usually this is solved by duplicating jars.
OSGi class loader
Class loader in OSGi is a
graph, unlike a hierarchical tree structure of Java and J2EE. Each bundle has
its own class loader. For example bundle B defines an export list and bundle A
imports it. When A requests a class to be loaded which is present in B, immediately
call will be delegated to the class loader of B. As there is no long list of
classes organized in a hierarchical structure anymore, it proves to be very
fast and efficient. The process of resolving dependency based on import and
export metadata is called resolution process. Its a complex procedure taken
care by the OSGi framework.
Inherent advantage of
having this kind of class loader is:
1. Export is defined with
a version attribute, where as imports can specify a range of versions. This
allows a bundle to say I need a library having a version between x and y.
2. All packages in a
bundle need not stick to a version while exporting. Package org.gok can be
exported under 0.9 and another package in the same bundle org.goh can be
exported as 1.1.
3. Same library having
different versions can be there in the application side by side
java.net.URLClassLoader
This class loader is used
to load classes and resources from a search path of URLs referring to both JAR
files and directories.
It can be used to download
classes from other directories or remote server.
Class loading is
fundamental to two of the most compelling features of Java: dynamic linking and
dynamic extension.
Dynamic linking allows
types to be incrementally incorporated into a running JVM.
Dynamic extension allows
the decision as to which types are loaded into a JVM to be deferred until
runtime. This means that an application hosted by the JVM can leverage types
that were previously unknown, or perhaps did not even exist, when the
application was compiled.
Custom class loader
Usually, J2EE servers
implement its custom class loader to enable features such as hot redeployment
and application independence.
Normally, the class
loaded by custom class loader is unknown to current class loader, we can't cast
the object to real type, we can refer it as an object or refer it using its
interface.
Basic steps to implement a custom class loader
We usually just need
override loadClass() or findClass() method.
1. Check whether
the class has already been loaded, if already loaded, just return it.
2. If not, check
to see whether its parent class loader can load this class.
3. If not,
implement the logic to load it by its own.
4. Define the class
(call parent's method directly).
5. Check whether
the class has to be resolved (linked), and in that case, resolve it (call
parent's method directly)..
6. Return the
Class object to the caller.
Example:
Resource