Explore the Java class loader in depth

Source: Internet
Author: User
Tags apache tomcat

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.ClassLoaderClass Introduction

java.lang.ClassLoaderThe 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.forNameis 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.forNamea 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

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.