The ClassLoader (class loader) is a very important concept in java™. The ClassLoader is responsible for loading the Java class's byte code into the Java virtual machine. In this paper, the basic concepts of Java ClassLoader are introduced in detail, including the proxy mode, the concrete process of loading class and the thread context class loader, and then how to develop their own classloader, and finally introduce the application of ClassLoader in Web container and osgi™.
The ClassLoader is an innovation in the Java language and one of the most important reasons for the popularity of the Java language. It enables Java classes to be dynamically loaded into a Java virtual machine and executed. The ClassLoader emerged from JDK 1.0 and was originally developed to meet the needs of Java applets. Java applets need to download Java class files from the remote to the browser and execute them. Class loaders are now widely used in Web containers and OSGi. In general, Java application developers do not need to interact directly with the same loader. The default behavior of the Java virtual machine is sufficient to meet the needs of most situations. However, if you encounter the need to interact with the ClassLoader, and the mechanism of the ClassLoader is not very well understood, it is easy to spend a lot of time to debug ClassNotFoundException
and NoClassDefFoundError
wait for exceptions. This article will give you a detailed introduction to the Java ClassLoader to help readers understand this important concept in the Java language. Here is a first introduction to some of the underlying concepts.
Class Loader Basic Concepts
As the name implies, the ClassLoader (class loader) is used to load Java classes into a Java virtual machine. In general, Java virtual machines use Java classes in the following way: Java source programs (. java files) are converted to Java bytecode (. class files) after being compiled by the Java compiler. The ClassLoader is responsible for reading the Java byte code and converting it into java.lang.Class
an instance of the class. Each such instance is used to represent a Java class. newInstance()
an object of the class can be created by means of this instance. The actual situation can be more complex, such as the Java byte code may be generated dynamically through the tool, or it may be downloaded over the network.
Basically all ClassLoader are java.lang.ClassLoader
an instance of the class. This Java class is described in detail below.
java.lang.ClassLoader
Class Introduction
java.lang.ClassLoader
The basic function of a class is to find or generate its corresponding byte code based on the name of a specified class, and then define a Java class from those byte codes, which is java.lang.Class
an instance of the class. In addition, it ClassLoader
is also responsible for loading the resources required for Java applications, such as files and configuration files. However, this article only discusses the functionality of its load class. In order to complete this function of the load class, a ClassLoader
series of methods are provided, and the more important methods are shown in table 1. The details of these methods are described below.
Table 1. Methods related to load classes in ClassLoader
Method |
Description |
getParent () |
Returns the class loader's parent class loader. |
loadclass (String name) |
loads the class with name name , and returns the result Java.lang.Class an instance of the class. |
findclass (String name) |
Find the class with name name , and return the result is Java.lang.Class an instance of the class. |
findloadedclass (String name) |
Find the already loaded class with name name , returning the result is a an instance of the Java.lang.Class class. |
defineclass (String name, byte[] B, int off, int len) |
byte array; b is converted to a Java class, and the result is an instance of the Java.lang.Class class. This method is declared as final . |
resolveclass (class<?> c) |
link to the specified Java class. |
For the method given in table 1, the value of the parameter representing the class name is the binary name
name of the class. It is important to note that the representation of the inner class, such as com.example.Sample$1
and com.example.Sample$Inner
other representations. These methods are described below for further instructions on how the ClassLoader works. The tree-like structure of the ClassLoader is described below.
The tree-like structure of the ClassLoader
The ClassLoader in Java can be broadly divided into two categories, one for the system, the other for Java application developers. There are three main types of ClassLoader available in the system:
- Boot class loader (bootstrap class loader): It is used to load Java's core library, which is implemented by native code and does not inherit from
java.lang.ClassLoader
.
- Extension class loader (Extensions class loader): It is used to load the Java extension library. The implementation of the Java virtual machine provides an extension library directory. The ClassLoader finds and loads the Java class in this directory.
- System class Loader: It loads Java classes according to the Classpath (CLASSPATH) of the Java application. In general, Java-applied classes are loaded by it. You can
ClassLoader.getSystemClassLoader()
get it by.
In addition to the system-provided ClassLoader, developers can java.lang.ClassLoader
implement their own ClassLoader by inheriting classes to meet some special requirements.
All Class loaders have a parent class loader, in addition to the boot class loader. It can be obtained by the method given in table 1 getParent()
. For the system-provided ClassLoader, the parent classloader of the system ClassLoader is the extension class loader, and the parent classloader of the extension ClassLoader is the boot class loader, and for a developer-written class loader, its parent classloader is the ClassLoader that loads the class loader Java class. Because the ClassLoader Java class is like any other Java class, it is also loaded by the ClassLoader. In general, the developer-written class loader's parent ClassLoader is the System class loader. The ClassLoader is organized in such a way that it forms a tree-like structure. The root node of the tree is the boot class loader. A typical class loader tree structure is shown in Figure 1, where the arrows point to the parent ClassLoader.
Figure 1. Class Loader tree-like organizational structure
Listing 1 shows the tree-like structure of the ClassLoader.
Listing 1. A tree-like structure showing the ClassLoader
public class Classloadertree {public static void Main (string[] args) { ClassLoader loader = Classloadertree.clas S.getclassloader (); while (loader! = null) { System.out.println (loader.tostring ()); Loader = Loader.getparent ();}} }
Each Java class maintains a reference to the ClassLoader that defines it, and getClassLoader()
this reference can be obtained by means of a method. The full parent ClassLoader is output in code Listing 1 by means getParent()
of a recursive call method. Code Listing 1 runs as shown in Listing 2.
Listing 2. Demonstrates the results of a tree-like organizational structure of the ClassLoader
[Email protected] [Email protected]
As shown in Listing 2, the first output is the class ClassLoaderTree
loader, the System class loader. It is an sun.misc.Launcher$AppClassLoader
instance of the class, and the second output is an extension class loader, which is an sun.misc.Launcher$ExtClassLoader
instance of a class. It is important to note that there is no output boot class loader, because some JDK implementations are returned to the parent class loader as the Boot class loader getParent()
null
.
After understanding the tree-like structure of the ClassLoader, the following describes the proxy mode of the ClassLoader.
Agent mode for Class loader
When the ClassLoader tries to find the byte code of a class and define it itself, it proxies it to its parent class loader, and the parent class loader tries to load the class first, and so on. Before introducing the motives behind the proxy pattern, it is first necessary to explain how the Java virtual machine determines that two Java classes are the same. The Java virtual machine depends not only on whether the full name of the class is the same, but also on whether the class loader loading this class is the same. The two classes are the same only if they are the same. Even the same byte code is different from the classes that are obtained after the loading of different classloader. A Java class, for example com.example.Sample
, generates a byte code file after compilation Sample.class
. Two different ClassLoader ClassLoaderA
and ClassLoaderB
read the Sample.class
file separately, and define an java.lang.Class
instance of two classes to represent the class. These two instances are not the same. For Java virtual machines, they are different classes. Attempting to assign values to objects of these two classes throws a run-time exception ClassCastException
. The following is an example of how to specify. The Java class is given in code Listing 3 com.example.Sample
.
Listing 3. Com.example.Sample class
Package com.example; public class Sample { private sample instance; public void Setsample (Object instance) { this.instance = (Sample) instance; } }
As shown in Listing 3, the com.example.Sample
method of the class setSample
takes a parameter of a java.lang.Object
type and casts the argument to a com.example.Sample
type. The code to test whether the Java class is the same is shown in Listing 4.
Listing 4. Test if the Java class is the same
public void testclassidentity () {String Classdatarootpath = "C:\\workspace\\classloader\\cl Assdata "; Filesystemclassloader FSCL1 = new Filesystemclassloader (Classdatarootpath); Filesystemclassloader Fscl2 = new Filesystemclassloader (Classdatarootpath); String className = "Com.example.Sample"; try {class<?> Class1 = Fscl1.loadclass (ClassName); Object obj1 = Class1.newinstance (); class<?> Class2 = Fscl2.loadclass (className); Object obj2 = Class2.newinstance (); Method Setsamplemethod = Class1.getmethod ("Setsample", Java.lang.Object.class); Setsamplemethod.invoke (Obj1, OBJ2); } catch (Exception e) {e.printstacktrace (); } }
The Code Listing 4 uses FileSystemClassLoader
two different instances of the class to load the class separately, com.example.Sample
obtains two different java.lang.Class
instances, newInstance()
and then generates two classes of objects by means of the method obj1
obj2
, and finally calls the object via the Java reflection API obj1
. Method setSample
that attempts to obj2
assign an object to obj1
an internal instance
object. Code Listing 4 runs as shown in Listing 5.
Listing 5. Test the Java class for the same run result
Java.lang.reflect.InvocationTargetException at Sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method) at Sun.reflect.NativeMethodAccessorImpl.invoke (nativemethodaccessorimpl.java:39) at Sun.reflect.DelegatingMethodAccessorImpl.invoke (DELEGATINGMETHODACCESSORIMPL.JAVA:25) at Java.lang.reflect.Method.invoke (method.java:597) at ClassLoader. Classidentity.testclassidentity (classidentity.java:26) at ClassLoader. Classidentity.main (classidentity.java:9) caused by:java.lang.ClassCastException:com.example.Sample cannot be cast to Com.example.Sample at Com.example.Sample.setSample (sample.java:7) ... 6 more
As you can see from the running results shown in Listing 5, the runtime throws an java.lang.ClassCastException
exception. Although the names of the two objects are the obj1
obj2
same as the classes, the two classes are loaded by different class loader instances and are not considered to be identical by the Java virtual machine.
Knowing this, you can understand the design motivation of the proxy pattern. The proxy mode is designed to ensure the type safety of the Java Core library. All Java applications require at least a reference java.lang.Object
class, which means that the java.lang.Object
class needs to be loaded into the Java Virtual machine at runtime. If this loading process is done by the Java application's own classloader, it is likely that there are multiple versions of the java.lang.Object
classes, and that the classes are incompatible. Through the proxy mode, the loading of the classes of the Java Core Library is accomplished by the bootstrap ClassLoader, which ensures that Java applications are all compatible with the same version of the Java Core Library.
Different Class loaders create additional namespaces for classes of the same name. Classes of the same name can and exist in the Java virtual machine, and only need to load them with a different class loader. Classes loaded by different classloader are incompatible, which is equivalent to creating a separate Java class space within the Java virtual machine. This technique is used in many frameworks and is described in more detail later.
The detailed procedure for class loader load classes is described below.
The process of loading a class
In the previous introduction of the class loader's proxy mode, it was mentioned that the class loader would first delegate to other ClassLoader to try to load a class. This means that the class loader that actually completes the loading of the class and the ClassLoader that starts the loading process may not be the same. The loading of a really complete class is implemented by invocation, defineClass
while the loading process of the startup class is implemented by invocation loadClass
. The former is called the definition loader for a class (defining loader), which is called the initial loader (initiating loader). When a Java virtual machine determines whether two classes are the same, the class's definition loader is used. That is, it is not important which class loader initiates the loading of the class, it is important to finally define the loader for this class. The correlation between the two kinds of loaders is that the definition loader for a class is the initial loader for the other classes it references. If com.example.Outer
a class refers to a class, the class's com.example.Inner
com.example.Outer
definition Loader is responsible for initiating com.example.Inner
the class's loading process.
Method loadClass()
throws an java.lang.ClassNotFoundException
exception, defineClass()
and the method throws an java.lang.NoClassDefFoundError
exception.
After the class loader successfully loads a class, the instance of the resulting java.lang.Class
class is cached. The next time the class is requested to load, the ClassLoader will use the instance of the cached class directly without attempting to load it again. That is, for a class loader instance, a class of the same full name is loaded only once, i.e. the loadClass
method is not called repeatedly.
Another class loader is discussed below: The thread context class loader.
Thread Context class Loader
The
Thread Context class loader (context class loader) was introduced starting with JDK 1.2. Class method in java.lang.Thread
; getcontextclassloader ()
and Setcontextclassloader (ClassLoader cl)
The context class loader used to get and set threads. If it is not set by Setcontextclassloader (ClassLoader cl)
method, the thread inherits the context class loader of its parent thread. The context class loader for the initial thread that the Java application runs is the system ClassLoader. Code that runs in a thread can load classes and resources through such loaders.
The
Proxy mode of the class loader mentioned earlier does not solve all of the classloader problems encountered in Java application development. Java provides a number of service provider interfaces (services Provider INTERFACE,SPI) that allow third parties to provide implementations for these interfaces. The common SPI is JDBC, JCE, JNDI, JAXP, and JBI. These SPI interfaces are provided by the Java Core library, such as the JAXP SPI interface definition contained in the javax.xml.parsers
package. The implementation code for these SPI is likely to be contained in a jar package that is dependent on the Java application, which can be found through the classpath (CLASSPATH), such as the jar package contained in the apache xerces that implements the JAXP SPI. The code in the SPI interface often needs to load specific implementation classes. such as in JAXP; in the javax.xml.parsers.DocumentBuilderFactory
class; newinstance ()
method is used to generate a new documentbuilderfactory
instance. The real class of the instance here is inherited from javax.xml.parsers.DocumentBuilderFactory
, provided by the implementation of the SPI. For example, in Apache Xerces, the implemented class is Org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
. The problem is that the SPI interface is part of the Java Core library and is loaded by the boot class loader, and the Java class implemented by the SPI is typically loaded by the System class loader. The boot ClassLoader is unable to find the SPI implementation class because it only loads the Java core library. It also cannot delegate to the system ClassLoader because it is the ancestor class loader of the system ClassLoader. In other words, the class loader's proxy mode does not solve this problem.
The thread context ClassLoader just solves this problem. If you do not make any settings, the context class loader for the Java app's thread is the system context class loader by default. Using the thread context class loader in the code of the SPI interface, you can successfully load the class to the SPI implementation. The thread context ClassLoader is used in many implementations of the SPI.
Another way to load a class is described below: Class.forName
.
Class.forName
Class.forName
is a static method that can also be used to load classes. There are two forms of the method: Class.forName(String name, boolean initialize, ClassLoader loader)
and Class.forName(String className)
. The first form of the parameter represents the full name of the class, whether the class is initialized, or the ClassLoader that is name
initialize
loader
used when loading. The second form is equivalent to setting the value of the parameter to initialize
true
loader
the class loader of the current class. Class.forName
a very common usage is when the database driver is loaded. such as Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance()
the driver used to load Apache Derby database.
After describing the basic concepts related to the ClassLoader, here's how to develop your own classloader.
Back to top of page
Develop your own class loader
Although in most cases, the class loader implementation provided by default can already meet the requirements. But in some cases, you still need to develop your own ClassLoader for your app. For example, your application transmits byte codes for Java classes over the network, and for security purposes, these byte codes are encrypted. At this point you will need your own classloader to read the encrypted byte code from a network address, then decrypt and validate, and finally define the classes to run in the Java virtual machine. The following is the development of the class loader with two specific examples.
File System class Loader
The first class loader is used to load the Java byte code stored on the file system. The complete implementation is shown in code listing 6.
Listing 6. File System class Loader
public class Filesystemclassloader extends ClassLoader {private String rootdir; Public Filesystemclassloader (String rootdir) {this.rootdir = RootDir; } protected class<?> Findclass (String name) throws ClassNotFoundException {byte[] Classdata = GETCLASSD ATA (name); if (Classdata = = null) {throw new ClassNotFoundException (); } else {return defineclass (name, Classdata, 0, classdata.length); }} private byte[] Getclassdata (String className) {String path = Classnametopath (ClassName); try {inputstream ins = new FileInputStream (path); Bytearrayoutputstream BAOs = new Bytearrayoutputstream (); int buffersize = 4096; byte[] buffer = new Byte[buffersize]; int bytesnumread = 0; while ((Bytesnumread = ins.read (buffer))! =-1) {baos.write (buffer, 0, bytesnumread); } return Baos.tobytearray (); } catch (IOException e) {e.printstacktrace (); } return null; } private String Classnametopath (string className) {return rootdir + File.separatorchar + CLA Ssname.replace ('. ', File.separatorchar) + ". Class"; } }
As shown in Listing 6, the class FileSystemClassLoader
inherits from the class java.lang.ClassLoader
. In common methods of the classes listed in Table 1 java.lang.ClassLoader
, in general, the class loader that you develop only needs to override the findClass(String name)
method. The method of the java.lang.ClassLoader
class encapsulates the implementation of the loadClass()
previously mentioned proxy pattern. The method will first invoke the findLoadedClass()
method to check if the class has been loaded, and if it has not, it will invoke the parent ClassLoader's loadClass()
method to try to load the class, and if the parent ClassLoader cannot load the class, call findClass()
the method to find the class. Therefore, in order to ensure that the class loader correctly implements the proxy pattern, it is best not to overwrite the method when developing its own classloader, loadClass()
but to overwrite the findClass()
method.
The method of the class FileSystemClassLoader
findClass()
first finds the class's byte code file (. class file) on the hard disk based on the full name of the class, then reads the contents of the file, and finally defineClass()
converts the byte code into java.lang.Class
an instance of the class by means of the method.
Network class Loader
The following is a network class loader that shows how to implement dynamic updates of components through the ClassLoader. The basic scenario is that the Java byte code (. Class) file is stored on the server and the client obtains the byte code and executes it over the network. When there is a version update, you only need to replace the file saved on the server. The class loader makes it easier to implement this requirement.
Class NetworkClassLoader
is responsible for downloading Java class byte code over the network and defining the Java class. It is implemented with FileSystemClassLoader
similar. After you NetworkClassLoader
have loaded a version of a class, there are generally two ways to use it. The first approach is to use the Java reflection API. Another approach is to use interfaces. It is important to note that the classes that are downloaded from the server cannot be referenced directly in the client code because the class loader for the client code cannot find these classes. The Java Reflection API allows you to invoke methods of the Java class directly. The way to use an interface is to put the class of the interface in the client, and load the classes from the server that implement the different versions of this interface. These implementation classes are used by the client through the same interface. The specific code of the network ClassLoader is shown in the download.
After describing how to develop your own ClassLoader, the following describes the relationship between the ClassLoader and the Web container.
Back to top of page
Class loader vs. Web container
For WEB applications running in the Java ee™ container, the class loader is implemented differently than a typical Java application. Different WEB containers are implemented differently. As with Apache Tomcat, each WEB application has a corresponding ClassLoader instance. The ClassLoader also uses the proxy mode, and the difference is that it is the first attempt to load a class if it cannot find a proxy to the parent class loader. This is contrary to the order of the generic ClassLoader. This is the recommended practice in the Java Servlet specification, which is designed to give the Web application its own class precedence over those provided by the Web container. One exception to this proxy pattern is that the class of the Java core library is not within the scope of the lookup. This is also to ensure the type safety of the Java Core library.
In most cases, developers of WEB apps do not need to consider the details associated with the ClassLoader. Here are a few simple principles:
- Each WEB application has its own Java class files and jar packages for the libraries that are used, respectively, under the
WEB-INF/classes
and WEB-INF/lib
directory.
- Java class files and jar packages that are shared by multiple apps, placed under directories shared by all Web applications, as specified by the Web container.
- Checks whether the class loader for the current class and the context class loader for the current thread are correct when a class-not-found error occurs.
After describing the relationship between the ClassLoader and the Web container, the following describes its relationship to OSGi.
Class Loaders and OSGi
Osgi™ is a dynamic module system on Java. It provides a service-oriented and component-based runtime environment for developers, and provides a standard way to manage the lifecycle of software. OSGi has been implemented and deployed on many products and has been widely supported in the open source community. Eclipse is built on the basis of OSGi technology.
Each module (bundle) in
OSGi contains Java packages and classes. The module can declare Java packages and classes (via import-package
) that it relies on for other modules that need to be imported (import), or export its own packages and classes for use by other modules (by export-package
). This means that some Java packages and classes in a module need to be able to be hidden and shared. This is achieved through the class loader mechanism that is specific to OSGi. Each module in OSGi has a corresponding class loader. It is responsible for loading the Java packages and classes that the module contains itself. When it needs to load classes of the Java core library (packages and classes beginning with Java
), it is proxied to the parent classloader (usually the startup ClassLoader) to complete. When it needs to load the imported Java class, it proxies the module to export the Java class to complete the load. Modules can also explicitly declare certain Java packages and classes, which must be loaded by the parent class loader. You only need to set the system property org.osgi.framework.bootdelegation
value.
The
assumes that there are two modules Bundlea and Bundleb, both of which have their own classloader Classloadera and Classloaderb. The class Com.bundleA.Sample
is included in Bundlea, and the class is declared as exported, which means it can be used by other modules. Bundleb declares the class Com.bundleA.Sample
provided by the import Bundlea and contains a class com.bundleB.NewSample
inherits from com.bundleA.Sample
. When Bundleb is started, its classloader classloaderb need to load class com.bundleB.NewSample
, which in turn requires the Load class Com.bundleA.Sample
. Because Bundleb declares the class com.bundleA.Sample
is imported, Classloaderb loads the class com.bundleA.Sample
The work agent for the Bundlea class loader Classloadera to export the class. Classloadera finds the class inside its module, com.bundleA.Sample
and defines it, the resulting class com.bundleA.Sample
Instances can be used by all declarations that import modules of this class. For classes that begin with Java
, they are loaded by the parent ClassLoader. If system attribute is declared, org.osgi.framework.bootdelegation=com.example.core.*
, then for package Com.example.core The classes in the
are done by the parent ClassLoader.
This class loader structure of the OSGi module allows the different versions of a class to coexist in a Java virtual machine, giving it a lot of flexibility. This difference, however, can be problematic for developers, especially if the module needs to use a third-party-provided library. Here are a few more good suggestions:
- If a class library is used by only one module, place the jar package of the class library in the module and specify it in
Bundle-ClassPath
.
- If a class library is shared by multiple modules, you can create a separate module for the class library and declare the Java packages that other modules need to be exported. Other module declarations import these classes.
- If the class library provides an SPI interface, and the thread context class loader is used to load the Java class implemented by the SPI, the Java class may not be found. If an
NoClassDefFoundError
exception occurs, first check that the context class loader for the current thread is correct. Thread.currentThread().getContextClassLoader()
This class loader can be obtained by passing it. The ClassLoader should be the class loader corresponding to the module. If not, you can first class.getClassLoader()
get the class loader corresponding to the module, and then Thread.currentThread().setContextClassLoader()
set the context class loader for the current thread.
Summarize
The ClassLoader is an innovation in the Java language. It makes it possible to dynamically install and update software components. This article details the topics related to ClassLoader, including basic concepts, proxy patterns, thread-context ClassLoader, relationships with Web containers and OSGi, and more. When a developer encounters ClassNotFoundException
and NoClassDefFoundError
waits for an exception, it should examine the class loader of the class that throws the exception and the context ClassLoader of the current thread, where the problem can be found. When developing your own classloader, you need to be aware of the coordination with the existing ClassLoader organization structure.
Explore the Java class loader in depth