In-depth discussion on Java class loaders

Source: Internet
Author: User
Tags apache tomcat

Introduction:Class loader is an important concept in Java. The class loader is responsible for loading the byte code of the Java class to the Java Virtual Machine. This article first introduces in detail the basic concepts of the Java class loader, including the proxy mode, the specific process of loading the class, and the thread context class loader, and then introduces how to develop your own class loader, finally, the application of the Class Loader in Web containers and OSGi is introduced.

 

Classloaders are an innovative Java language and an important reason for the popularity of Java. It enables the Java class to be dynamically loaded to the Java Virtual Machine and executed. The Class Loader emerged from JDK 1.0 and was initially developed to meet the needs of Java Applet. The Java Applet needs to download and execute a Java class file from a remote browser. Currently, class loaders are widely used in Web containers and OSGi. Generally, Java application developers do not need to directly interact with similar loaders. The default behavior of Java virtual machines is enough to meet the needs of most cases. However, if you need to interact with the class loader and do not know much about the class loader mechanism, it is easy to spend a lot of time debugging.ClassNotFoundExceptionAndNoClassDefFoundError. This article will introduce Java classloaders in detail to help readers deeply understand this important concept in Java. The following describes some basic concepts.

Basic concepts of classloaders

As the name implies, the class loader is used to load Java classes into Java virtual machines. Generally, Java virtual machines use Java classes as follows: Java source programs (. java files) are converted to Java Byte Code (. class files) after being compiled by the Java compiler ). The Class Loader reads the Java byte code and converts itjava.lang.ClassClass. Each such instance is used to represent a Java class. ThenewInstance()Method to create an object of this class. The actual situation may be more complex. For example, Java byte code may be dynamically generated by tools or downloaded over the network.

Basically, all the class loaders arejava.lang.ClassLoaderClass. The following describes the Java class in detail.

java.lang.ClassLoaderClass Introduction

java.lang.ClassLoaderThe basic responsibility of a class is to find or generate the corresponding byte code based on the name of a specified class, and then define a Java class from these byte code, that isjava.lang.ClassClass. In addition,ClassLoaderIt is also responsible for loading resources required for Java applications, such as files and configuration files. However, this article only discusses the function of loading classes. To fulfill the responsibility of loading classes,ClassLoaderProvides a series of methods, as shown in table 1. The details of these methods are described below.

Table 1. methods related to the loading class in ClassLoader

Method Description
getParent() Returns the parent class loader of the class loader.
loadClass(String name) Load Name:nameClass, the returned result isjava.lang.ClassClass.
findClass(String name) The search name isnameClass, the returned result isjava.lang.ClassClass.
findLoadedClass(String name) The search name isnameThe returned result isjava.lang.ClassClass.
defineClass(String name, byte[] b, int off, int len) Put the byte arraybAnd the returned result isjava.lang.ClassClass. This method is declaredfinal.
resolveClass(Class<?> c) Link to the specified Java class.

For the method given in Table 1nameThe parameter value is the binary name of the class. Note the representation of internal classes, suchcom.example.Sample$1Andcom.example.Sample$Inner. The following describes the working mechanism of the class loader. The following describes the tree structure of the class loader.

Tree Structure of the Class Loader

The class loaders in Java can be roughly divided into two types: one is provided by the system, and the other is compiled by Java application developers. The system provides the following class loaders:

  • Bootstrap class loader: it is used to load the core library of Java. It is implemented using native code and does not inherit fromjava.lang.ClassLoader.
  • Extensions class loader: used to load Java extension libraries. The Java virtual machine provides an extension library directory. This class loader searches for and loads Java classes in this directory.
  • System class loader: it loads Java classes according to the CLASSPATH of Java applications. Generally, Java application classes are loaded by them. You can useClassLoader.getSystemClassLoader()To obtain it.

In addition to the class loaders provided by the system, developers can inheritjava.lang.ClassLoaderTo meet some special requirements.

In addition to the bootstrap loader, all classloaders have a parent loader. UsegetParent()Method. For the class loader provided by the system, the parent class loader of the system class loader is the extension class loader, and the parent class loader of the extension class loader is the bootstrap class loader; for a class loader compiled by developers, its parent class loader is a class loader that loads the Java class of this class loader. Because the Class Loader Java class, like other Java classes, also needs to be loaded by the class loader. Generally, the parent class loader of the Class Loader compiled by developers is the system class loader. The class loader is organized in this way to form a tree structure. The root node of the tree is the bootstrap loader. Figure 1 shows a typical tree structure of the Class Loader. the arrow points to the parent class loader.

Figure 1. Tree Structure of the Class Loader

Code List 1 demonstrates the tree structure of the class loader.

Listing 1. Demonstrate the tree structure of the Class Loader

 public class ClassLoaderTree {     public static void main(String[] args) {         ClassLoader loader = ClassLoaderTree.class.getClassLoader();         while (loader != null) {             System.out.println(loader.toString());             loader = loader.getParent();         }     }  }

Each Java class maintains a reference pointing to the class loader that defines it.getClassLoader()Method to obtain the reference. Recursive call in code list 1getParent()Method to output all parent class loaders. The running result of code list 1 is shown in code list 2.

Listing 2. Demonstrate the running results of the tree structure of the Class Loader

 sun.misc.Launcher$AppClassLoader@9304b1  sun.misc.Launcher$ExtClassLoader@190d11

As shown in code list 2, the first output isClassLoaderTreeClass Loader, that is, the system class loader. It issun.misc.Launcher$AppClassLoaderClass; the second output is the extended class loader, which issun.misc.Launcher$ExtClassLoaderClass. Note that the bootstrap loader is not output here, because some JDK implementations use the bootstrap loader for the parent loader,getParent()Method returnnull.

After learning about the tree structure of the Class Loader, the following describes the proxy mode of the class loader.

Agent Mode of the Class Loader

When the class loader tries to find and define the byte code of a class, it will first proxy to its parent class loader, and the parent class loader will first try to load this class, and so on. Before introducing the motives behind the proxy mode, you must first explain how the Java Virtual Machine judges that the two Java classes are the same. The Java virtual machine not only needs to check whether the full name of the class is the same, but also whether the class loader to load this class is the same. Only when the two are the same can the two classes be considered the same. Even for the same byte code, the classes obtained after being loaded by different class loaders are also different. For example, a Java classcom.example.SampleThe byte code file is generated after compilation.Sample.class. Two different class loadersClassLoaderAAndClassLoaderBReadSample.classFile, and define twojava.lang.ClassClass. The two instances are different. For Java virtual machines, they are different classes. Attempts to assign values to the objects of these two classes will throw a runtime exceptionClassCastException. The following is an example. The Java class is shown in code list 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 code listing 3,com.example.SampleClass MethodsetSampleAccept onejava.lang.ObjectType parameter, which is forcibly convertedcom.example.SampleType. Shows code listing 4 to test whether Java classes are the same.

Listing 4. Test whether Java classes are the same

 public void testClassIdentity() {     String classDataRootPath = "C:\\workspace\\Classloader\\classData";     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 class is used in code list 4.FileSystemClassLoaderTo load classes separately.com.example.Sample, Two differentjava.lang.ClassAnd thennewInstance()Methods generate two classes of objects respectivelyobj1Andobj2Finally, through the Java reflection API in the objectobj1OnsetSample, Trying to put the objectobj2Assignedobj1InternalinstanceObject. The running result of code listing 4 is shown in code listing 5.

Listing 5. test whether the Java class has the same running 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 

The running result in code list 5 shows thatjava.lang.ClassCastExceptionException. Although two objectsobj1Andobj2But these two classes are loaded by different class loaders, so they are not considered the same by Java virtual machines.

After understanding this, you can understand the design motivation of the proxy mode. The proxy mode is used to ensure the type security of the Java core library. All Java applications must reference at leastjava.lang.ObjectClass, that is, when running,java.lang.ObjectThis class needs to be loaded into the Java Virtual Machine. If this loading process is completed by the Java application's class loader, there may be multiple versionsjava.lang.ObjectAnd these classes are incompatible. In the proxy mode, the class loading of the Java core library is completed by the bootstrap Class Loader. This ensures that all Java applications use classes of the same version of the Java core library, is compatible with each other.

Different classloaders create additional namespaces for classes with the same name. Classes with the same name can exist in Java virtual machines. You only need to use different class loaders to load them. Classes loaded by different classloaders are incompatible, which is equivalent to creating mutually isolated Java class spaces in the Java Virtual Machine. This technology is used in many frameworks and will be detailed later.

The following describes the detailed process of loading a class by the class loader.

Process of loading classes

When we introduced the proxy mode of the Class Loader, we mentioned that the class loader will first attempt to load a class to other class loaders. This means that the class loader that truly completes the loading of the class and the class loader that starts the loading process may not be the same. To load a class, calldefineClassAnd the loading process of the startup class is through callingloadClass. The former is called the definition loader of a class, and the latter is called the initialization loader ). When the Java Virtual Machine determines whether the two classes are the same, it uses the class definition loader. That is to say, it is not important to start the loading process of the class. What is important is to define the loader of the class. The association between the two types of loaders is that the definition loader of a class is the initial loader of other classes it references. For examplecom.example.OuterReferenced classcom.example.InnerBy Classcom.example.OuterIs responsible for starting the class.com.example.Inner.

MethodloadClass()Yesjava.lang.ClassNotFoundExceptionException; MethoddefineClass()Yesjava.lang.NoClassDefFoundErrorException.

After the classloader successfully loads a classjava.lang.ClassClass. The next time you request to load the class, the Class Loader directly uses the cached class instance instead of trying to load the class again. That is to say, for a Class Loader instance, classes with the same full name are loaded only once, that isloadClassMethods are not repeatedly called.

Next we will discuss another kind of loaders: thread context class loaders.

Thread context Class Loader

Context class loader is introduced from JDK 1.2. Classjava.lang.ThreadMethod ingetContextClassLoader()AndsetContextClassLoader(ClassLoader cl)The context class loader used to obtain and set the thread. IfsetContextClassLoader(ClassLoader cl)The thread inherits the context class loader of its parent thread. The context class loader of the initial thread of the Java application is the system class loader. The code running in the thread can use this class loader to load classes and resources.

The proxy mode of the Class Loader mentioned above does not solve all the problems encountered in Java application development. Java provides many Service Provider Interfaces (SPI), allowing third parties to implement these interfaces. Common SPI include JDBC, JCE, JNDI, JAXP, and JBI. These SPI interfaces are provided by the Java core library. For example, JAXP's SPI interface definition is included injavax.xml.parsersPackage. These SPI implementation codes are probably included as jar packages that Java applications depend on. They can be found through CLASSPATH, for example, implement the jar package contained in Apache Xerces of jaxp spi. The code in the SPI interface often needs to load specific implementation classes. For example, in JAXPjavax.xml.parsers.DocumentBuilderFactoryClassnewInstance()Method to generate a newDocumentBuilderFactory. The real class of the instance here is inherited fromjavax.xml.parsers.DocumentBuilderFactoryProvided by the SPI implementation. For example, in Apache Xerces, the implemented class isorg.apache.xerces.jaxp.DocumentBuilderFactoryImpl. The problem is that the SPI interface is part of the Java core library and loaded by the boot Class Loader. the Java class implemented by SPI is generally loaded by the system class loader. The bootstrap loader cannot find the SPI implementation class because it only loads the Java core library. It cannot be used as a proxy to the system class loader because it is the ancestor class loader of the system class loader. That is to say, the proxy mode of the Class Loader cannot solve this problem.

The thread context loader solves this problem. If no settings are made, the context class loader of the Java application thread is the system context class loader by default. You can use the thread context class loader in the SPI interface code to successfully load the class to the SPI implementation. The thread context loader is used in many SPI implementations.

Next we will introduce another method for loading classes:Class.forName.

Class. forName

Class.forNameIs a static method and can also be used to load classes. This method has two forms:Class.forName(String name, boolean initialize, ClassLoader loader)AndClass.forName(String className). Parameters in the first formnameIndicates the full name of the class;initializeIndicates whether to initialize the class;loaderIndicates the Class Loader used for loading. The second form is equivalent to setting parameters.initializeIstrue,loaderThe value is the class loader of the current class.Class.forNameIs a common usage when loading the database driver. For exampleClass.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance()The driver used to load the Apache Derby database.

After introducing the basic concepts related to the class loader, the following describes how to develop your own class loader.

Back to Top

Develop your own class loaders

In most cases, the Class Loader implementation provided by the system by default can meet the requirements. However, in some cases, you still need to develop your own class loader for the application. For example, your application transmits Java-Class byte code over the network. To ensure security, these byte codes are encrypted. At this time, you need your own class loader to read the encrypted byte code from a network address, and then perform decryption and verification, finally, define the class to run in the Java Virtual Machine. The following two examples are used to describe the development of the class loader.

File System Class Loader

The first class loader is used to load the Java byte code stored in the file system. Complete implementation is shown in code listing 6.

Listing 6. File System class loaders

 public class FileSystemClassLoader extends ClassLoader {     private String rootDir;     public FileSystemClassLoader(String rootDir) {         this.rootDir = rootDir;     }     protected Class<?> findClass(String name) throws ClassNotFoundException {         byte[] classData = getClassData(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                 + className.replace('.', File.separatorChar) + ".class";     }  }

As shown in code listing 6FileSystemClassLoaderInherited from Classjava.lang.ClassLoader. Thejava.lang.ClassLoaderIn common methods of classes, generally, the self-developed class loaders only need to overwritefindClass(String name)Method.java.lang.ClassLoaderClass MethodloadClass()Encapsulates the implementation of the proxy mode mentioned above. This method is called first.findLoadedClass()Method to check whether the class has been loaded. If it has not been loaded, it will callloadClass()Method to load the class. If the parent class loader cannot load the class, callfindClass()Method to find the class. Therefore, to ensure that all classloaders correctly implement the proxy mode, it is recommended that you do not override the class loaders when developing your own class loaders.loadClass()Method, but OverwritefindClass()Method.

ClassFileSystemClassLoaderOffindClass()The method first searches for the byte code file (. class file) of the class on the hard disk based on the full name of the class, then reads the content of the file, and finally passesdefineClass()To convert the byte codejava.lang.ClassClass.

Network Class Loader

The following describes how to use a network class loader to dynamically update components. That is, the basic scenario is that the Java byte code (. class) file is stored on the server, and the client obtains and executes the byte code through the network. When a version is updated, you only need to replace the files saved on the server. This requirement can be easily implemented through the class loader.

ClassNetworkClassLoaderDownloads Java Class byte code over the network and defines Java classes. Its implementation andFileSystemClassLoaderSimilar. InNetworkClassLoaderAfter a certain version of the class is loaded, there are two methods to use it. The first method is to use the Java reflection API. Another approach is to use interfaces. Note that classes downloaded from the server cannot be referenced directly in the client code, because the class loaders of the client code cannot find these classes. Java reflection APIs can be used to directly call Java class methods. The interface is used to place the Interface Class in the client, and load classes of different versions implementing this interface from the server. These implementation classes are used on the client through the same interface. For more information about network class loaders, see download.

After introducing how to develop your own class loaders, the following describes the relationship between the class loaders and Web containers.

Back to Top

Classloaders and Web containers

For Web applications running in Java EE containers, the implementation of the Class Loader is different from that of General Java applications. Different Web containers are implemented in different ways. For Apache Tomcat, each Web application has a corresponding class loader instance. The Class Loader also uses the proxy mode. The difference is that it first tries to load a class. If it cannot be found, it then acts as a proxy to the parent class loader. This is in the opposite order of the general class loader. This is a recommended practice in the Java Servlet specification, and its purpose is to make the Web application's own class have a higher priority than the class provided by the Web container. An exception to this proxy mode is that the classes in the Java core library are not within the search scope. This is also to ensure the type security of the Java core library.

In most cases, Web application developers do not need to consider the details related to the class loader. Below are some simple principles:

  • Each Web application's own Java class file and the jar package of the library used are placed inWEB-INF/classesAndWEB-INF/libDirectory.
  • Java class files and jar packages shared by multiple applications are placed under the directory specified by the Web container to be shared by all Web applications.
  • When a class error cannot be found, check whether the class loader of the current class and the context class loader of the current thread are correct.

After introducing the relationship between the class loader and the Web container, the following describes the relationship between the class loader and the OSGi.

Back to Top

Class loaders and OSGi

OSGi is a dynamic module system in Java. It provides a service-oriented and component-based runtime environment for developers and provides a standard way to manage the software lifecycle. OSGi has been implemented and deployed on many products and has been widely supported in the open-source community. Eclipse is built based on OSGi technology.

Each module (bundle) in OSGi contains Java packages and classes. The module can declare the Java packages and classes of other modules on which it depends (import)Import-Package), You can also declare your own packages and classes for use by other modules (throughExport-Package). That is to say, you need to be able to hide and share some Java packages and classes in a module. This is implemented through the unique Class Loader mechanism of OSGi. Each module in OSGi has a corresponding class loader. It is responsible for loading the Java packages and classes contained by the module itself. When it needs to load the class of the Java core library (javaIt will proxy to the parent class loader (usually start the Class Loader. When it needs to load the imported Java class, it will proxy to the module that exports this Java class to complete the loading. The module can also explicitly declare some Java packages and classes, which must be loaded by the parent class loader. You only need to set System Propertiesorg.osgi.framework.bootdelegation.

Assume that two modules bundleA and bundleB have their own class loaders classLoaderA and classLoaderB. Include classes in bundleAcom.bundleA.SampleAnd the class is declared as exported, that is, it can be used by other modules. BundleB declares the class provided by importing bundleA.com.bundleA.SampleAnd contains a classcom.bundleB.NewSampleInherited fromcom.bundleA.Sample. When bundleB is started, Its Class Loader classLoaderB needs to load the classcom.bundleB.NewSampleTo load the classcom.bundleA.Sample. Because bundleB declares the classcom.bundleA.SampleYes. classLoaderB loads the class.com.bundleA.SampleTo the classloader classLoaderA that exports bundleA of this class. ClassLoaderA searches for classes in its modulecom.bundleA.SampleAnd define it to get the classcom.bundleA.SampleThe instance can be used by all declared modules that have imported such data. ForjavaClasses started with are all loaded by the parent class loader. If the system attribute is declaredorg.osgi.framework.bootdelegation=com.example.core.*, Then for the packagecom.example.coreClasses in are all completed by the parent class loader.

This type of Loader structure of the OSGi module makes it possible for different versions of a class to coexist in Java virtual machines, resulting in great flexibility. However, this difference may also cause some trouble for developers, especially when modules need to use third-party libraries. Below are some good suggestions:

  • If a Class Library only uses one module, put the jar package of the class library in the moduleBundle-ClassPath.
  • If a class library is shared by multiple modules, you can create a separate module for this class library and declare the Java packages required by other modules as exported. Other modules declare to import these classes.
  • If the Class Library provides the SPI interface and uses the thread context class loader to load the Java class implemented by SPI, the Java class may not be found. IfNoClassDefFoundErrorException. First, check whether the context class loader of the current thread is correct. PassThread.currentThread().getContextClassLoader()You can get the class loader. The class loader should be the class loader corresponding to this module. If not, useclass.getClassLoader()To obtain the Class Loader corresponding to the module.Thread.currentThread().setContextClassLoader()To set the context class loader for the current thread.

Back to Top

Summary

Classloaders are an innovative Java language. It makes it possible to dynamically install and update software components. This article describes in detail the related topics of the Class Loader, including basic concepts, proxy mode, thread context class loader, and the relationship with Web containers and OSGi. When developers encounterClassNotFoundExceptionAndNoClassDefFoundErrorWhen exceptions occur, check the class loaders that throw exceptions and the context class loaders of the current thread to find the problem. When developing your own classloaders, you must be aware of the coordination with the existing classloaders.

 

From: http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html

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.