Learn more about JAVAclassloader

Source: Internet
Author: User
Learn more about JAVAclassloader-general Linux technology-Linux programming and kernel. Unlike a program written in C or C ++, a Java program is not an executable file, but composed of many independent class files. Each file corresponds to a Java class.

In addition, these class files are not immediately loaded into the memory, but the memory is loaded according to the program's needs. ClassLoader is the part of the JVM that loads classes into the memory.

Moreover, Java ClassLoader is written in Java. This means that it is very easy to create your own ClassLoader without having to understand the minor details of JVM.


Why write ClassLoader?

If the JVM already has a ClassLoader, why write another one? Good question. The default ClassLoader only knows how to mount class files from the local file system. However, this is only suitable for general cases, that is, the Java program has been fully compiled and the computer is in the waiting state.

But the most innovative thing about Java is that JVM can easily retrieve classes from non-local hard disks or from the network. For example, a browser can use a custom ClassLoader to load executable content from a Web site.

There are many other ways to obtain class files. In addition to simply loading files from a local or network, you can use a custom ClassLoader to complete the following tasks:

Automatically verifies the digital signature before executing the untrusted code

Use the password provided by the user to transparently decrypt the code

Dynamically create custom build classes that meet specific user needs

Any content that you think can generate a Java bytecode can be integrated into the application.

Custom ClassLoader example

If you have used JDK or any Applet viewer based on a Java browser, you must have used a custom ClassLoader.

One of the most exciting things about Sun's first release of the Java language was watching how the new technology executes code loaded from a remote Web server at runtime. (In addition, there is even more exciting thing-the Java technology provides a powerful language that facilitates code writing .) What's even more exciting is that it can execute bytecode sent from a remote Web server over an HTTP connection.

This feature is attributed to the ability to install custom ClassLoader in Java. The Applet viewer contains a ClassLoader, which does not look for classes in the local file system. Instead, it accesses the Web site on the remote server and loads the original bytecode file through HTTP, and convert them into classes in JVM.

ClassLoaders in browsers and Applet viewers can also do other things: they support security and enable different applets to run on different pages without interfering with each other.

The Echidna written by Luke Gorrie is an open source package that enables you to run multiple Java applications on a single virtual machine. It uses a custom ClassLoader to provide each application with its own copy of such files to prevent mutual interference between applications.

ClassLoader example

After learning how ClassLoader works and how to write ClassLoader, we will create a Classloader called CompilingClassLoader (CCL. CCL compiles Java code for us without interfering with this process. It is basically similar to the "make" program directly built into the runtime system.

Note: before learning more, you should note that some aspects of the ClassLoader System (Java 2 platform) have been improved in JDK 1.2 ). This tutorial is based on JDK 1.0 and 1.1, but it can also be run in a later version.

ClassLoader changes in Java 2 describe the changes in Java version 1.2 and provide detailed information for modifying ClassLoader to take advantage of these changes.

The basic goal of ClassLoader is to provide services for class requests. When the JVM needs to use a Class, it requests this Class from ClassLoader according to the name, and then ClassLoader tries to return a Class object that represents this Class. You can create a custom ClassLoader by overwriting methods corresponding to different stages of the process.

In the rest of this article, you will learn the key methods of Java ClassLoader. You will understand the role of each method and how it fits into the process of loading class files. You will also know what code you need to write when creating your ClassLoader.

In the following section, you will use this knowledge to use CompilingClassLoader, The ClassLoader example.

Method loadClass

ClassLoader. loadClass () is the entry point of ClassLoader. The features are as follows:

Class loadClass (String name, boolean resolve );

The name parameter specifies the name of the class required by JVM, which is expressed in package notation, such as Foo or java. lang. Object. The resolve parameter indicates whether the method needs to parse the class. Before preparing the execution class, consider class resolution. Resolution is not always required. If the JVM only needs to know whether the class exists or find out the super class of the class, it does not need to be parsed.

In Java 1.1 and earlier versions, the loadClass method is the only method to override when creating a custom ClassLoader. (ClassLoader changes in Java 2 provide information about the findClass () method in Java 1.2 .)

Method defineClass

The defineClass method is the main tool of ClassLoader. This method accepts an array composed of the original bytes and converts it to a Class object. The original array contains data loaded from a file system or network.

DefineClass manages many of the complex, mysterious, and implementation-dependent aspects of JVM-It analyzes bytecode into runtime data structures, verifies validity, and so on. Don't worry, you don't need to write it yourself. In fact, even if you want to do so, you cannot overwrite it because it has been marked as final.

Method findSystemClass

The findSystemClass method Loads files from the local file system. It looks for a Class file in the local file system. If it exists, it will use defineClass to convert the original byte to a Class object to convert the file to a Class. When running a Java application, this is the default mechanism for JVM to load classes normally. (ClassLoader changes in Java 2 provide detailed information about the process changes in Java 1.2 .)

For a custom ClassLoader, you can use findSystemClass only after you try other methods to load the class. The reason is simple: ClassLoader is responsible for executing the special steps for loading classes, not for all classes. For example, even if the ClassLoader loads some classes from a remote Web site, a large number of basic Java libraries need to be installed on the local machine. These classes are not of our concern, so JVM should load them by default: from the local file system. This is the use of findSystemClass.

The workflow is as follows:

Request a custom ClassLoader to load the class.

Check the remote Web site to see if any classes are required.

If yes, It is better. capture this class to complete the task.

If no, if this class is in the Basic Java library, call findSystemClass to load the class from the file system.

In most custom ClassLoaders, The findSystemClass is called first to save the time it takes to search for many Java library classes that can be loaded locally. However, as we can see in the next section, JVM loads classes from the local file system until we are sure that our application code can be automatically compiled.

Method resolveClass

As mentioned above, classes can be loaded not completely (without resolution), or completely (with resolution. When writing our own loadClass, you can call resolveClass, depending on the resolve parameter value of loadClass.

Method findLoadedClass

FindLoadedClass acts as a cache: When loadClass is requested to load the class, it calls this method to check whether the class has been loaded, which can avoid the trouble of re-loading the existing class. Call this method first.

Assembly

Let's take a look at how to assemble all the methods.

The following is an example of loadClass implementation. (Here, we do not specify the technology used to generate class files-it can be installed from Net, extracted from archive files, or compiled in real time. No matter which one, it is a special magic way that we get the bytes of the original class file .)

CCL decryption

Our ClassLoader (CCL) task is to ensure that the Code is compiled and updated.

The following describes how it works:

When requesting a category, first check whether it is in the current directory or corresponding subdirectory of the disk.

If the class does not exist, but the source code does, the Java compiler is called to generate class files.

If the class already exists, check whether it is older than the source code. If yes, call the Java compiler to regenerate class files.

If the compilation fails or the class file cannot be generated from the existing source code for other reasons, ClassNotFoundException is returned.

If the class still does not exist, it may be in other libraries, so we call findSystemClass to find the class.

If no, ClassNotFoundException is returned.

Otherwise, the class is returned.

Call findLoadedClass to check whether a mounted class exists.

If not, use the special magic method to obtain the original bytes.

If the original bytes exist, call defineClass to convert them into Class objects.

If no original bytes exist, call findSystemClass to check whether the class is obtained from the local file system.

If the resolve parameter is true, call resolveClass to parse the Class object.

If no class exists, ClassNotFoundException is returned.

Otherwise, return the class to the caller.

How Java compilation works

Before further discussion, we should take a step back to discuss Java compilation. Generally, the Java compiler does not just compile the class you require it to compile. It also compiles other classes if these classes are required by the classes you want to compile.

CCL compiles each class in the application one by one. But in general, after the compiler finishes compiling the first class, CCL will find all the classes to be compiled and then compile it. Why? The Java compiler is similar to the rule we are using: if the class does not exist or is older than its source code, it needs to be compiled. In fact, the Java compiler does most of the work before CCL.

When CCL compiles them, it reports the classes on the application it is compiling. In most cases, CCL will call the compiler on the main class of the program, and it will finish all the work to be done-a single call to the compiler is enough.

However, some classes are not compiled in the first step. If you use the Class. forName method to load classes by name, the Java compiler will not know what this Class requires. In this case, you will see that CCL runs the Java compiler again to compile this class. This process is demonstrated in the source code.

Use CompilationClassLoader

To use CCL, you must call the program in a special way. You cannot directly run this program, for example, % java Foo arg1 arg2.

Run it as follows:

% Java CCLRun Foo arg1 arg2

CCLRun is a special stub program. It creates CompilingClassLoader and uses it to load the main class of the program to ensure that the entire program is loaded through CompilingClassLoader. CCLRun uses the Java Reflection API to call the main method of a specific class and pass the parameter to it. For more information, see source code.

Running example

The source code includes a group of small classes, which demonstrate the working method. The main program is the Foo class, which creates an instance of the class Bar. Class Bar creates an instance of another Class Baz in the baz package to show how CCL handles the code in the sub-package. Bar is also loaded by name, and its name is Boo, which is used to show that it can also work with CCL.

Each class declares that it has been loaded and run. Use the source code to try it out. Compile CCLRun and CompilingClassLoader. Make sure that you do not compile other classes (Foo, Bar, Baz, and Boo). Otherwise, CCL will not be used because these classes have already been compiled.

% Java CCLRun Foo arg1 arg2

CCL: Compiling Foo. java...

Foo! Arg1 arg2

Bar! Arg1 arg2

Baz! Arg1 arg2

CCL: Compiling Boo. java...

Boo!

Note: first, call the compiler, Foo. java to manage Bar and baz. Baz. It is called until Bar loads Boo by name, then CCL will call the compiler again to compile it.

CompilingClassLoader. java

The source code of CompilingClassLoader. java is as follows:

// $ Id $

Import java. io .*;

/*

A CompilingClassLoader compiles your Java source on-the-fly. It checks

For nonexistent. class files, or. class files that are older than their

Corresponding source code.

*/

Public class CompilingClassLoader extends ClassLoader

{

// Given a filename, read the entirety of that file from disk

// And return it as a byte array.

Private byte [] getBytes (String filename) throws IOException {

// Find out the length of the file

File file = new File (filename );

Long len = file. length ();

// Create an array that's just the right size for the file's

// Contents

Byte raw [] = new byte [(int) len];

// Open the file

FileInputStream fin = new FileInputStream (file );

// Read all of it into the array; if we don't get all,

// Then it's an error.

Int r = fin. read (raw );

If (r! = Len)

Throw new IOException ("Can't read all," + r + "! = "+ Len );

// Don't forget to close the file!

Fin. close ();

// And finally return the file contents as an array

Return raw;

}

// Spawn a process to compile the java source code file

// Specified in the 'javafile' parameter. Return a true if

// The compilation worked, false otherwise.

Private boolean compile (String javaFile) throws IOException {

// Let the user know what's going on

System. out. println ("CCL: Compiling" + javaFile + "...");

// Start up the compiler

Process p = runtime.getruntime(cmd.exe c ("javac" + javaFile );

// Wait for it to finish running

Try {

P. waitFor ();

} Catch (InterruptedException ie) {System. out. println (ie );}

// Check the return code, in case of a compilation error

Int ret = p. exitValue ();

// Tell whether the compilation worked

Return ret = 0;

}

// The heart of the ClassLoader -- automatically compile

// Source as necessary when looking for class files

Public Class loadClass (String name, boolean resolve)

Throws ClassNotFoundException {

// Our goal is to get a Class object

Class clas = null;

// First, see if we 've already dealt with this one

Clas = findLoadedClass (name );

// System. out. println ("findLoadedClass:" + clas );

// Create a pathname from the class name

// E. g. java. lang. Object => java/lang/Object

String fileStub = name. replace ('.','/');

// Build objects pointing to the source code (. java) and object

// Code (. class)

String javaFilename = fileStub + ". java ";

String classFilename = fileStub + ". class ";

File javaFile = new File (javaFilename );

File classFile = new File (classFilename );

// System. out. println ("j" + javaFile. lastModified () + "c" +

// ClassFile. lastModified ());

// First, see if we want to try compiling. We do if (a) there

// Is source code, and either (b0) there is no object code,

// Or (b1) there is object code, but it's older than the source

If (javaFile. exists ()&&

(! ClassFile. exists () |

JavaFile. lastModified ()> classFile. lastModified ())){

Try {

// Try to compile it. If this doesn't work, then

// We must declare failure. (It's not good enough to use

// And already-existing, but out-of-date, classfile)

If (! Compile (javaFilename) |! ClassFile. exists ()){

Throw new ClassNotFoundException ("Compile failed:" + javaFilename );

}

} Catch (IOException ie ){

// Another place where we might come to if we fail

// To compile

Throw new ClassNotFoundException (ie. toString ());

}

}

// Let's try to load up the raw bytes, assuming they were

// Properly compiled, or didn't need to be compiled

Try {

// Read the bytes

Byte raw [] = getBytes (classFilename );

// Try to turn them into a class

Clas = defineClass (name, raw, 0, raw. length );

} Catch (IOException ie ){

// This is not a failure! If we reach here, it might

// Mean that we are dealing with a class in a library,

// Such as java. lang. Object

}

// System. out. println ("defineClass:" + clas );

// Maybe the class is in a library -- try loading

// The normal way

If (clas = null ){

Clas = findSystemClass (name );

}

// System. out. println ("findSystemClass:" + clas );

// Resolve the class, if any, but only if the "resolve"

// Flag is set to true

If (resolve & clas! = Null)

ResolveClass (clas );

// If we still don't have a class, it's an error

If (clas = null)

Throw new ClassNotFoundException (name );

// Otherwise, return the class

Return clas;

}

}



CCRun. java

The source code of CCRun. java is as follows:



// $ Id $

Import java. lang. reflect .*;

/*

CCLRun executes a Java program by loading it through

CompilingClassLoader.

*/

Public class CCLRun

{

Static public void main (String args []) throws Exception {

// The first argument is the Java program (class) the user

// Wants to run

String progClass = args [0];

// And the arguments to that program are just

// Arguments 1. n, so separate those out

// Their own array

String progArgs [] = new String [args. length-1];

System. arraycopy (args, 1, progArgs, 0, progArgs. length );

// Create a CompilingClassLoader

CompilingClassLoader ccl = new CompilingClassLoader ();

// Load the main class through our CCL

Class clas = ccl. loadClass (progClass );

// Use reflection to call its main () method, and

// Pass the arguments in.

// Get a class representing the type of the main method's argument

Class mainArgType [] = {(new String [0]). getClass ()};

// Find the standard main method in the class

Method main = clas. getMethod ("main", mainArgType );

// Create a list containing the arguments -- in this case,

// An array of strings

Object argsArray [] = {progArgs };

// Call the method

Main. invoke (null, argsArray );

}

}



Foo. java

The source code of Foo. java is as follows:



// $ Id $

Public class Foo

{

Static public void main (String args []) throws Exception {

System. out. println ("foo! "+ Args [0] +" "+ args [1]);

New Bar (args [0], args [1]);

}

}



Bar. java

The source code of Bar. java is as follows:



// $ Id $

Import baz .*;

Public class Bar

{

Public Bar (String a, String B ){

System. out. println ("bar! "+ A +" "+ B );

New Baz (a, B );

Try {

Class booClass = Class. forName ("Boo ");

Object boo = booClass. newInstance ();

} Catch (Exception e ){

E. printStackTrace ();

}

}

}



Baz/Baz. java

The source code of baz/Baz. java is as follows:



// $ Id $

Package baz;

Public class Baz

{

Public Baz (String a, String B ){

System. out. println ("baz! "+ A +" "+ B );

}

}



Boo. java

The source code of Boo. java is as follows:



// $ Id $

Public class Boo

{

Public Boo (){

System. out. println ("Boo! ");

}

}
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.