Learn from Java ClassLoader
What is CLASSLOADER?
In a popular commercial programming language, Java language is different from the Java Virtual Machine (JVM). This means that the compiled program is a special, independent of the platform, is not dependent on the machine they run. To a large extent, this format is different from the traditional executable program format.
Unlike C or C , the Java program is not an executable file, but consists of many independent class files, each file corresponds to a Java class. In addition, these types of documents are not immediately loaded into memory, but the memory needs to be loaded according to the program. ClassLoader is part of the JVM that will be loaded into memory. Moreover, Java ClassLoader is written in Java language. This means that you are very easy to create your own ClassLoader, you don't have to know the tiny detail of JVM.
Why write ClassLoader?
If the JVM already has a classloader, then why should I write another? Asked well. The default classloader only knows how to load a class file from the local file system. However, this is only suitable for normal conditions, that is, all Java programs have been compiled, and the computer is waiting.
But the most new thing in the Java language is that JVM can be very easy to get from those non-local hard drives or from the network. For example, the viewer can load the executable content from the Web site using custom ClassLoader.
There are many other ways to get class files. In addition to simply load files from local or network, you can use custom ClassLoader to complete the following tasks: Before performing non-confidence code, automatically verify that digital signatures use the user's password transparently decrypt code dynamically created customization of user-specific needs. Chemical buildings Anything you think can generate Java bytecodes can be integrated into applications.
Custom ClassLoader Example
If you use the JDK or any Applet viewer in a Java browser, you are almost certainly using custom ClassLoader.
When Sun originally releases a Java language, the most exciting thing is to watch this new technology how to perform code from the remote web server from the remote web server. (In addition, there are more exciting things - Java technology provides a powerful language that is easy to write code.) More exciting is that it can perform bytecodes sent from the remote web server via HTTP connection .
This feature is attributed to the Java language to install custom ClassLoader. The Applet viewer contains a ClassLoader, which is not looking for classes in the local file system, but to access the Web site on the remote server, load the original byte code file through HTTP, and convert them into the JVM.
ClassLoaders in your browser and Applet viewers can also do other things: they support security and make different applets do not interfere with each other on different pages.
Luke Gorrie written Echidna is an open source package that allows you to run multiple Java applications on a single virtual machine. It uses custom ClassLoader to prevent applications from interfering with each application by providing each application to each application.
Our ClassLoader Example
Understand how ClassLoader works and how to write ClassLoader, we will create a ClassLoader called CompilingClassLoader (CCL). CCL compiles our Java code without requiring our interference. It basically is similar to the "Make" program that directly constructs directly to the runtime system. Note: Before you learn more, you should pay attention to some aspects of the ClassLoader system (ie, Java 2 Platform) in JDK version 1.2. This tutorial is written by JDK version 1.0 and 1.1, but can also be run in later versions. ClassLoader changes in Java 2 describes changes in Java version 1.2 and provides some details to modify ClassLoader to utilize these changes.
The basic goal of ClassLoader is to serve the request of the class. When the JVM needs to use classes, it requests this class based on the name, and then ClassLoader tries to return a class object that represents this class. Customized ClassLoader can be created by covering a different phase of this process.
In the rest of this article, you will learn the key ways of Java ClassLoader. You will understand how the role of each method and how it is suitable for loading the class file. You will also know how code is written when you create your own classloader.
In the following, you will take advantage of our ClassLoader Example - CompilingClassLoader.
Method LOADCLASS
ClassLoader.LoadClass () is the entry point of ClassLoader. The characteristics are as follows:
Class loadClass (String name, Boolean Resolve);
The Name parameter specifies the name of the class required by the JVM, which is represented by a package representation, such as foo or java.lang.Object. The resolve parameter tells the method whether you need to parse classes. Class analysis should be considered before preparing to execute the class. It doesn't always need to be parsed. If JVM only needs to know if this class exists or finds the superclass of this class, then no resolution.
In Java version 1.1 and previous versions, the LoadClass method is the only way to create custom ClassLoader. (ClassLoader changes in Java 2 provides information about the FindClass () method in Java 1.2.)
DEFINECLASS
The DefineClass method is the main 诀窍 of ClassLoader. This method accepts arrays consisting of original bytes and converts it to Class objects. The original array contains data that is installed from a file system or network.
DefineClass manages many of the complex, mysterious and reliance on the implementation of the JVM - it analyzes the bytecode to the runtime data structure, verification effectiveness, etc. Don't worry, you don't have to write it yourself. In fact, even if you want to do this, you can't overwrite it because it has been marked into the end.
Method FINDSYSTEMCLASS
The FindSystemClass method is loaded from the local file system. It looks for class files in the local file system. If there is, use defineClass to convert the original byte into a Class object to convert the file into classes. This is the default mechanism for JVM normally loaded when running a Java application. (ClassLoad in Java 2 provides details about the process of this process of Java version 1.2.)
For custom ClassLoaders, only FindsystemClass is used after trying other methods to load classes. The reason is very simple: ClassLoader is responsible for performing special steps to carry out the laying class, not responsible for all classes. For example, even if ClassLoad is loaded from a remote Web site, you still need to load a large number of basic Java libraries on your local machine. These classes are not what we have cared, so JVM is to load them in the default manner: from the local file system. This is the use of FindsystemClass. The workflow is as follows: Request customized ClassLoader load class. Check the remote web site to see if there is a need. If so, so good; grab this class, complete the task. If not, assume that this class is in the basic Java library, then call FindSystemClass to load it from the file system.
In most custom ClassLoaders, FindSystemClass first calls FindSystemClass to save many Java library classes that can be loaded locally and to find the time spent on remote Web sites. However, as seen in the next section, the JVM will be loaded from the local file system when it is confident that the application code can be automatically compiled.
Method ResolVeclass
As mentioned earlier, it can be loaded into classes in not completely (without parsing), or it can be completely (with parsing). When writing our own loadClass, resolveclass can be called, depending on the value of the LoadClass's Resolve parameter.
FindlineedClass
FindloadedClass acts as a cache: When requesting loadClass to load classes, it calls this method to see if ClassLoad is loaded, which avoids the trouble of reloading existing classes. This method should be called first.
Assemble
Let us look at how to assemble all methods. Our LoadClass implementation examples perform the following steps. (Here, we don't specify which technique that uses a class file - it can be loaded from NET, or extract from the archive file, or real-time compilation. No matter which kind, it is a special magical way Make us the original file byte.)
CCL revealed
Our ClassLoader (CCL) task is to ensure that the code is compiled and updated. The following describes its working mode: When requesting a class, first check if it is currently in the current directory or corresponding subdirectories. If this class does not exist, but the source code is there, then the Java compiler is called to generate a class file. If the class already exists, check if it is older than the source code. If so, call the Java compiler to regenerate class files. If the compilation failed, or because other reasons cannot generate class files from the existing source code, return ClassNotFoundException. If there is still no such class, maybe it is in other libraries, so call FindSystemClass to find the class. Returns ClassNotFoundException if not yet. Otherwise, returning to this class. Call FindLoadedClass to see if there is a class that has been loaded. If not, then use the special magical way to get the original byte. If you already have the original byte, call DefineClass converts them to the Class object. If there is no original byte, then call FindSystemClass to see if you get the class from the local file system. If the resolve parameter is TRUE, call ResolVeclass to resolve the Class object. Returns ClassNotFoundException if there is no class. Otherwise, return the class to the call. Java compiled work mode
Before discussing, you should take a step forward and discuss Java compilation. Typically, the Java compiler is not just compiling a class that requires it to be compiled. It also compiles other classes, and if these classes are the classes you need to be compiled. CCL compiles every class that needs to be compiled one by one. However, in general, after compiler compiles the first class, the CCL will find all classes that need to be compiled, and then compile it. why? The Java compiler is similar to the rules we are using: if the class does not exist, or it is older than its source code, it needs to be compiled. In fact, the Java compiler is a step before the CCL, which will do most of the work.
When CCL is compiled, it is reported which application is being compiled. In most cases, the CCL will call the compiler on the primary class in the program, which will do all the single calls of the compiler to do.
However, there is a situation that does not compile certain classes when the first step is. If you use the Class.Forname method, you will be loaded through the name, and the Java compiler does not know what this class is required. In this case, you will see that the 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 can't run the program directly, such as:% Java Foo Arg1 arg2 should run it in the following way:% java cclrun foo arg1 arg2
CClrun is a special settle program that creates CompiLingClassLoader and uses it to load the program's primary class to ensure that the entire program is loaded through CompiLingClassLoader. CclRun uses the Java Reflection API to call a specific class's primary method and pass the parameters to it. For more information, see the source code.
Run example
The source code includes a group of small classes that demonstrate how to work. The main program is a Foo class that creates an instance of class bar. Class BAR creates an instance of another class BAZ, which is in the Baz package, which is to show how the CCL handles the code in the sub-package. Bar is also loaded by name, its name is Boo, which is used to show it to work with CCL. Each class declares that has been loaded and run. Now use the source code to try it. Compile CCLRUN and COMPILINGCLASSLOADER. Make sure do not compile other classes (Foo, Bar, Baz, and Boo), otherwise CCL will not be used because these classes have been compiled.
% Java cclrun foo arg1 arg2 ccl: compling foo.java ... foo! arg1 arg2 bar! arg1 arg2 baz! arg1 arg2 ccl: compiling boo.java ... boo!
Please note that the compiler is first called, foo.java manages BAR and Baz.baz. Until Bar is loaded with BOO through the name, it is called, and the CCL will call the compiler again to compile it. CompiLingClassLoader.java
The following is the source code of CompilingClassLoader.java
// $ 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 thing file from disk
// and return it as a byte arch.
Private Byte [] getBytes (String filename) throws oews oException {
// 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 to the array; if we don't get all,
// theen it's an error.
INT R = Fin.read (RAW);
IF (r! = len)
Throw new oException ("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
// The compilation workd, false Otherwise.
Private Boolean Compile (String Javafile) throws oewception {
// let the user know what's going on
System.out.println ("CCL: Compiable" JavaFile "...");
// Start Up The Compiler
Process p = runtime.getime (). EXEC ("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 Compiration Error
Int ret = p.exitvalue ();
// Tell WHether the Compiration WORKED
Return Ret == 0;
}
// The Heart of The ClassLoader - Automatic or Compile
// Source As Necessary When Looking for Class Files
Public Class LoadClass (String Name, Boolean Resolve)
Throws classnotfoundexception {
// out to get a class object
Class clas = NULL;
// first, see if we've alleady 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 compling. We do if (a) there /// is source code, and each (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
// Weme Must Declare Failure. (It's Not Good Enough To Use
// and already-extra, 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
// 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 ife "resolve"
// Flag is set to True
IF (Resolve && Clas! = NULL)
ResolVeclass (Clas);
// if West 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 following is the source code of ccrun.java
// $ 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 this program area JUST
// arguments 1..n, So Separate Those Out Into
// THEIR OWN ARRAY
String progargs [] = new string [args.lendth-1];
System.Arraycopy (Args, 1, Progargs, 0, Progargs.Length);
// Create a Compiable ClassLoader
CompiableingClassLoader CCL = New CompilingClassLoader ();
// load the main class through cor CCL
Class clas = ccl.loadClass (progclass);
// use reflection to call its main () Method, And to
// pass the arguments in.
// Get a class representing the Type of the main method's argument
Class maingType [] = {(new string [0]). GetClass ()};
// Find The Standard Main MAIN METHOD in The Class
Method main = Clas.getMethod ("main", maingtype;
// 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 following is the source code of foo.java
// $ 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 following is the source code of bar.java
// $ 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 following is the source code of Baz / Baz.java
// $ ID $
Package baz;
Public Class Baz
{
Public Baz (String a, string b) {
System.out.println ("BAZ!" A " B);
}
}
Boo.java
The following is the source code of Boo.java
// $ ID $
Public class boo
{
Public boo () {
System.out.println ("BOO!");
}
}