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 and other 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 an instance of the Java.lang.Class class. Each such instance is used to represent a Java class. An object of the class can be created by using the Newinstance () method 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 an instance of the Java.lang.ClassLoader class. This Java class is described in detail below. Java.lang.ClassLoader Class Introduction
The basic function of the Java.lang.ClassLoader class is to find or generate its corresponding byte code based on the name of a given class, and then define a Java class from those byte codes, which is an instance of the Java.lang.Class class. In addition, ClassLoader is responsible for loading the resources needed for Java applications, such as files and configuration files. However, this article only discusses the functionality of its load class. To complete this responsibility for the load class, ClassLoader provides a series of methods to compare important methods such as
GetParent () returns the class loader's parent class loader.
LoadClass (String name) loads the class named name, and the returned result is an instance of the Java.lang.Class class.
Findclass (String name) looks for the class with name, and the result is an instance of the Java.lang.Class class.
Findloadedclass (String name) looks for a class that has already been loaded named name, and the result is an instance of the Java.lang.Class class.
DefineClass (String name, byte[] B, int off, int len) converts the contents of byte array B into a Java class, and the result returned is an instance of the Java.lang.Class class. This method is declared final.
Resolveclass (class<?> c) links the specified Java class.
The given method, the value of the name parameter that represents the class name is the binary name of the class. It is important to note the representations of the inner classes, such as com.example.sample$1 and Com.example.sample$inner. 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 with 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. It can be obtained by Classloader.getsystemclassloader ().
In addition to the system-provided classloader, developers can implement their own classloader by inheriting the Java.lang.ClassLoader class 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 GetParent () method given in table 1. 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.
Public class Classloadertree { publicstaticvoid main (string[] args) { = Classloadertree. class . getClassLoader (); while NULL ) { System.out.println (loader.tostring ()); = loader.getparent ();}} }
Each Java class maintains a reference to the ClassLoader that defines it, which can be obtained through the getClassLoader () method. In code Listing 1, the GetParent () method is called recursively to output all of the parent ClassLoader. 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 loader for the Classloadertree class, the system ClassLoader. It is an instance of the Sun.misc.launcher$appclassloader class, and the second output is an extension class loader, which is an instance of the Sun.misc.launcher$extclassloader class. It is important to note that there is no output boot class loader, because some JDK implementations for the parent ClassLoader are the boot ClassLoader, and the GetParent () method returns 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 com.example.Sample, for example, generates a byte code file sample.class after compiling. Two different ClassLoader Classloadera and Classloaderb read the Sample.class file separately and define two instances of the Java.lang.Class class 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 Com.example.Sample is given in code Listing 3.
Public class Sample { private sample instance; Public void Setsample (Object instance) { this. Instance = (Sample) instance; } }
As shown in Listing 3, the method of the Com.example.Sample class Setsample accepts a parameter of type Java.lang.Object and casts the argument to the Com.example.Sample type. The code to test whether the Java class is the same is shown in Listing 4.
Public voidtestclassidentity () {String Classdatarootpath= "D:\\workspace\\normaltools\\bin"; Filesystemclassloader FSCL1=NewFilesystemclassloader (Classdatarootpath); Filesystemclassloader Fscl2=NewFilesystemclassloader (Classdatarootpath); String ClassName= "Com.koubei.test.load.Sample"; Try{Class<?> Class1 =Fscl1.findclass (className); Object obj1=class1.newinstance (); Class<?> Class2 =Fscl2.findclass (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 two different instances of class Filesystemclassloader to load class Com.example.Sample separately, obtains two instances of different java.lang.Class, and then passes Newinstance () The method generates two classes of objects Obj1 and OBJ2, and finally calls the method Setsample on the object obj1 through the Java reflection API, attempting to assign the object Obj2 to Obj1 object inside instance. Code Listing 4 runs as shown in Listing 5.
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 26 9 caused by: Java.lang.ClassCastException:com.example.Sample cannot is 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 a Java.lang.ClassCastException exception. Although the names of the two objects obj1 and obj2 are the same, 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 need at least a reference to the 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 class, and that these 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 actual loading of a class is done by calling DefineClass, while the loading process of the startup class is implemented by calling 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. such as class Com.example.Outer refers to class Com.example.Inner, the definition loader of the class Com.example.Outer is responsible for initiating the loading process of the class Com.example.Inner.
The method LoadClass () throws a Java.lang.ClassNotFoundException exception, and the method DefineClass () throws a Java.lang.NoClassDefFoundError exception.
After the class loader successfully loads a class, the resulting instance of the 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, that is, 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. The methods in class Java.lang.Thread Getcontextclassloader () and Setcontextclassloader (ClassLoader cl) are used to get and set the context class loader for the thread. If it is not set by the 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 class loader's proxy pattern 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 packages contained in the Apache Xerces that implements the JAXP SPI. The code in the SPI interface often needs to load specific implementation classes. The Newinstance () method in the Javax.xml.parsers.DocumentBuilderFactory class in JAXP is used to generate a new instance of Documentbuilderfactory. The real class of instances here is inherited from Javax.xml.parsers.DocumentBuilderFactory, provided by the implementation of the SPI. As 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.
Here's another way to load a class: Class.forName.
Class.forName
Class.forName is a static method that can also be used to load classes. There are two forms of this method: Class.forName (String name, Boolean initialize, ClassLoader loader), and Class.forName (string className). The first form of the parameter name represents the full name of the class, initialize indicates whether the class is initialized, and loader represents the class loader used when loading. The second form is equivalent to setting the value of the parameter initialize to True,loader as the class loader of the current class. A very common usage of class.forname is when the database driver is loaded. such as Class.forName ("Org.apache.derby.jdbc.EmbeddedDriver"). Newinstance () is used to load the Apache Derby database driver.
After describing the basic concepts related to the ClassLoader, here's how to develop your own classloader.
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.
Public classFilesystemclassloaderextendsClassLoader {PrivateString RootDir; PublicFilesystemclassloader (String rootdir) { This. RootDir =RootDir; } protectedClass<?> Findclass (String name)throwsClassNotFoundException {byte[] Classdata =getclassdata (name); if(Classdata = =NULL) { Throw Newclassnotfoundexception (); } Else { returnDefineClass (name, Classdata, 0, classdata.length); } } Private byte[] Getclassdata (String className) {string path=Classnametopath (className); Try{InputStream ins=NewFileInputStream (path); Bytearrayoutputstream BAOs=NewBytearrayoutputstream (); intBufferSize = 4096; byte[] buffer =New byte[buffersize]; intBytesnumread = 0; while((Bytesnumread = ins.read (buffer))! =-1) {baos.write (buffer,0, Bytesnumread); } returnBaos.tobytearray (); } Catch(IOException e) {e.printstacktrace (); } return NULL; } Privatestring Classnametopath (String className) {returnRootDir +File.separatorchar+ classname.replace ('. ', File.separatorchar) + ". Class"; } }
As shown in Listing 6, the class Filesystemclassloader inherits from the class Java.lang.ClassLoader. In the common methods of the Java.lang.ClassLoader class listed in table 1, in general, the class loader you developed only needs to overwrite the Findclass (String name) method. The method LoadClass () of the Java.lang.ClassLoader class encapsulates the implementation of the previously mentioned proxy pattern. The method first calls the Findloadedclass () method to check if the class has been loaded, and if it has not, it calls the parent ClassLoader's LoadClass () method to try to load the class, and if the parent ClassLoader cannot load the class, call Findclass ( ) 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 LoadClass () method when developing its own classloader, but to overwrite the Findclass () method.
The Findclass () method of the class Filesystemclassloader first finds the class's byte code file (. class file) on the hard disk based on the full name of the class, and then reads the contents of the file, and finally converts the byte code into a. DefineClass () method. An instance of the Java.lang.Class class.
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.
The class Networkclassloader is responsible for downloading Java class byte code over the network and defining the Java class. Its implementation is similar to Filesystemclassloader. After loading a version of a class through networkclassloader, 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.
Class loader vs. Web container
For running in Java EE? For WEB applications in containers, the ClassLoader 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 the jar packages of the libraries used, placed under the web-inf/classes and Web-inf/lib directories, respectively.
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. A module can declare Java packages and classes (via Import-package) of other modules that it relies on to import (imports), or export its own packages and classes for use by other modules (via 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 the class of the Java core library (packages and classes beginning with Java), it will be 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 value of the system property org.osgi.framework.bootdelegation.
Suppose there are two modules Bundlea and Bundleb, each of which has its own class loader Classloadera and Classloaderb. The class Com.bundleA.Sample is included in the 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 inherited from Com.bundleA.Sample. When Bundleb starts, its classloader classloaderb need to load class com.bundleB.NewSample, which in turn requires the class Com.bundleA.Sample to be loaded. Since Bundleb declares that the class com.bundleA.Sample is imported, classloaderb the work agent that loads the class com.bundleA.Sample to the Bundlea ClassLoader Classloadera that exports the class. Classloadera finds the class Com.bundleA.Sample inside its module and defines it, the resulting class com.bundleA.Sample instance can be used by all the modules that declare the class to be imported. For classes that begin with Java, they are loaded by the parent ClassLoader. If the system attribute org.osgi.framework.bootdelegation=com.example.core.* is declared, then the class in package Com.example.core is 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 the 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 a Noclassdeffounderror exception occurs, first check that the context class loader for the current thread is correct. The ClassLoader can be obtained by Thread.CurrentThread (). Getcontextclassloader (). The ClassLoader should be the class loader corresponding to the module. If not, the class loader corresponding to the module can be obtained first through Class.getclassloader (), and then by Thread.CurrentThread (). Setcontextclassloader () To 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. Developers who encounter exceptions such as ClassNotFoundException and Noclassdeffounderror should examine the class loader of the class that throws the exception and the context ClassLoader for 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.
Transferred from: http://xjtom78.iteye.com/blog/898882
Java's Calssloader