Understanding the Java ClassLoader Mechanism |
Java ClassLoader |
2006-5-23 |
|
Java.lang.noclassdeffounderror:com/sun/tools/javac/main
The most recent problem when using Java Dynamic compilation is that when using class Com.sun.tool.javac.Main, there are always noclassdeffounderror errors, and then we find the following articles, analysis, This may be due to a load problem with the package Tools.jar, although I declare the package in Classpath, there is always a run-time exception in the eclipse environment, for the correct compile time, run-time exception, Eclipse is generally caused by its own loading mechanism. Under Eclipse, for general Java Engineering, as long as the system set up the Classpath, in which the addition of Tools.jar package, you can; for plugin project, I was going to Tools.jar package, Direct copy to this project, and referenced in the property, and in the meta-inf/manifest. MF file in the runtime page of the CLASSPATH added this Tool.jar package, so that when the runtime is not abnormal, you can compile the normal. When the JVM (Java Virtual machine) starts, it forms an initial class loader hierarchy consisting of three classloader:
Bootstrap ClassLoader
|
Extension ClassLoader
|
System ClassLoader
Bootstrap ClassLoader-Boot (also known as the original) ClassLoader, which is responsible for loading Java core classes. In the Sun's JVM, the-xbootclasspath option is used in executing Java commands or the-D option is used to specify Sun.boot.class.path system property values to specify additional classes. This loader is very special, it is not actually a subclass of Java.lang.ClassLoader, but is implemented by the JVM itself. You can get bootstrap ClassLoader load the core class libraries by executing the following code:
Url[] Urls=sun.misc.launcher.getbootstrapclasspath (). Geturls ();
for (int i = 0; i < urls.length; i++) {
System.out.println (Urls.toexternalform ());
}
The results on my computer are:
File:/c:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
File:/c:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
File:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
File:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xercesimpl-2.0.0.jar
File:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
File:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
File:/c:/j2sdk1.4.1_01/jre/lib/rt.jar
File:/c:/j2sdk1.4.1_01/jre/lib/i18n.jar
File:/c:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
File:/c:/j2sdk1.4.1_01/jre/lib/jsse.jar
File:/c:/j2sdk1.4.1_01/jre/lib/jce.jar
File:/c:/j2sdk1.4.1_01/jre/lib/charsets.jar
File:/c:/j2sdk1.4.1_01/jre/classes
Now you know why we don't need to specify these classes in the System attribute classpath, because the JVM loads them automatically when it starts.
Extension ClassLoader-The Extended class loader, which is responsible for loading the class packages of jars in the extended directory of the JRE (Java_home/jre/lib/ext or specified by the Java.ext.dirs system attribute). This provides a standard mechanism for introducing new features other than the Java core classes. Because the default extended directory is common to all JVMs that are started from the same JRE, the jar class package placed in this directory is visible to all JVMs and system ClassLoader. Invoking Method GetParent () on this instance always returns null value because the boot loader bootstrap ClassLoader is not a true ClassLoader instance. So when you execute the following code:
System.out.println (System.getproperty ("Java.ext.dirs"));
ClassLoader Extensionclassloader=classloader.getsystemclassloader (). GetParent ();
System.out.println ("The parent of Extension ClassLoader:" +extensionclassloader.getparent ());
The results are:
C:\j2sdk1.4.1_01\jre\lib\ext
The parent of extension classloader:null
Extension ClassLoader is the parent of system ClassLoader, and Bootstrap ClassLoader is the parent of extension ClassLoader, But it is not an actual classloader, so null.
System ClassLoader-Systems (also known as application) ClassLoader, which is responsible for loading the-classpath or Java.class.path system attributes from the command Java when the JVM is started, or Classpath the jar class package and classpath specified by the operating system properties. The class loader can always be found by static method Classloader.getsystemclassloader (). If not specifically specified, any user-defined ClassLoader will use the class loader as its parent loader. Execute the following code to obtain:
System.out.println (System.getproperty ("Java.class.path"));
The output result is the classpath that the user sets in the system properties.
The ClassLoader loading class uses a wholly responsible delegation mechanism. The so-called overall responsibility, that is, when a classloader load a class, this class depends on and the reference of all classes are also loaded by this classloader, unless it is explicitly using another ClassLoader load The delegate mechanism is to have the parent (parent) class loader (not super, which is not an inheritance relationship with the parent ClassLoader Class) Looking for it, only to find it from its classpath when parent cannot find it. In addition to the class load also uses the cache mechanism, that is, if the cache saved this class is directly returned to it, if not only from the file read and converted into class, and stored cache, which is why we modified class but must restart the JVM to be effective.
The process of loading class per ClassLoader is:
1. Detect if this class is loaded (that is, whether there is this class in the cache), if there is to 8, if not to 2
2. If parent ClassLoader does not exist (no parent, that parent must be bootstrap ClassLoader), to 4
3. Request parent ClassLoader to load, if successful to 8, unsuccessful to 5
4. Request the JVM to load from the bootstrap ClassLoader, if successful to 8
5. Find the class file (from the classpath associated with this classloader). If not found, then to 7.
6. Load class from file, to 8.
7. Throw classnotfoundexception.
8. Return class.
Of these 5.6 steps, we can implement our own loading strategy by overwriting the ClassLoader Findclass method. Even overlay the LoadClass method to implement your own loading process.
The order of the class loaders is:
First bootstrap ClassLoader, then extension classloader, and finally system ClassLoader. You will find that the more important the loaded class is in front of you. The reason for this is for security reasons, just imagine if System ClassLoader "personally" loads the consequences of a destructive "Java.lang.System" class. This delegate mechanism ensures that the user, even with one such class, adds it to the classpath, but it is never loaded, because the class is always loaded by bootstrap ClassLoader. You can execute the following code:
System.out.println (System.class.getClassLoader ());
You will see that the result is null, which indicates that Java.lang.System was loaded by bootstrap ClassLoader because the bootstrap ClassLoader is not a real classloader instance, but is implemented by the JVM , as has been said before.
Let's look at how the JVM is going to build the structure of the ClassLoader for us:
Sun.misc.Launcher, as the name suggests, when you execute a Java command, the JVM first loads and initializes a Launcher using bootstrap ClassLoader, executing the code:
System.out.println ("The Launcher ' s ClassLoader is" +sun.misc.launcher.getlauncher (). GetClass (). getClassLoader ());
The results are:
The Launcher ' s classloader is null (because it is loaded with bootstrap ClassLoader, so class loader is null)
Launcher Initializes a good class loader structure based on system and command settings, which the JVM uses to get extension ClassLoader and system ClassLoader and load all classes that need to be loaded. Finally executes the class with the static main method specified by the Java command. Extension ClassLoader is actually an instance of the Sun.misc.launcher$extclassloader class, and system ClassLoader is actually sun.misc.launcher$ An instance of the Appclassloader class. and are all Java.net.URLClassLoader subclasses.
Let's look at some of the code for the launcher process.
Part of Launcher Code:
public class Launcher {
Public Launcher () {
Extclassloader Extclassloader;
try {
Initialize extension ClassLoader
Extclassloader = Extclassloader.getextclassloader ();
catch (IOException IOException) {
throw new Internalerror ("Could not create extension class loader");
}
try {
Initializing system classloader,parent is extension classloader
Loader = Appclassloader.getappclassloader (Extclassloader);
catch (IOException Ioexception1) {
throw new Internalerror ("Could not create application class loader");
}
Set system ClassLoader to the context ClassLoader of the current thread (described later)
Thread.CurrentThread (). Setcontextclassloader (loader);
......
}
Public ClassLoader getClassLoader () {
Return to System ClassLoader
return loader;
}
}
Part of Extension ClassLoader code:
Static Class Launcher$extclassloader extends URLClassLoader {
public static Launcher$extclassloader Getextclassloader ()
Throws IOException
{
File afile[] = Getextdirs ();
Return (Launcher$extclassloader) accesscontroller.doprivileged (new launcher$1 (Afile));
}
private static file[] Getextdirs () {
Get system Property "Java.ext.dirs"
String s = system.getproperty ("Java.ext.dirs");
File afile[];
if (s!= null) {
StringTokenizer StringTokenizer = new StringTokenizer (s, file.pathseparator);
int i = Stringtokenizer.counttokens ();
Afile = new File;
for (int j = 0; J < i; J + +)
AFILE[J] = new File (Stringtokenizer.nexttoken ());
} else {
Afile = new File[0];
}
return afile;
}
}
Part of the Code for system ClassLoader:
Static class Launcher$appclassloader extends URLClassLoader
{
public static ClassLoader Getappclassloader (ClassLoader ClassLoader)
Throws IOException
{
Get system Property "Java.class.path"
String s = system.getproperty ("Java.class.path");
File afile[] = s!= null? LAUNCHER.ACCESS$200 (s): New file[0];
Return (Launcher$appclassloader) accesscontroller.doprivileged (new launcher$2 (S, Afile, ClassLoader));
}
}
Look at the source code everyone is clear, extension ClassLoader is using the system property "Java.ext.dirs" to set the class search path, and there is no parent. The system ClassLoader is set to the class search path using the "Java.class.path" of the systems property and has a parent classloader. Launcher initializes the extension Classloader,system ClassLoader and sets the system ClassLoader to the context ClassLoader, but only returns system ClassLoader to the JVM.
How come out here again a context ClassLoader. What's the use of it? When we create thread threads, we can specify a suitable classloader for this thread as the context ClassLoader of this thread, and when this thread is running, the Setcontextclassloader method. We can use the Getcontextclassloader method to get this context ClassLoader to load the class we need. The default is System ClassLoader. Using this feature, we can "break" the ClassLoader delegate mechanism, the parent ClassLoader can get the current thread of the context ClassLoader, and this context ClassLoader can be its son classloader or other ClassLoader, then the parent ClassLoader can obtain the required Class from it, which breaks the limit that can only be requested from the parent ClassLoader. This mechanism can satisfy the class that is loaded by system ClassLoader (that is, in the JVM classpath) when our classpath is determined at runtime and is loaded by custom ClassLoader ClassLoader gets the custom classloader and loads it into a specific class (usually abstract classes and interfaces, which are implemented in custom ClassLoader), such as the servlet in Web applications that is loaded with this mechanism.
Well, now that we understand the structure and how the ClassLoader works, how do we implement dynamic loading and updating at runtime? As long as we can dynamically change the class search path and clear ClassLoader cache has loaded the class on the line, there are two scenarios, one is that we inherit a classloader, covering the LoadClass method, Dynamically looking for class files and using the DefineClass method, and the other is very simple and practical, just reuse a new class search path to new one classloader on the line, so that the class search path is updated to load the new class, A blank cache has also been regenerated (of course, the class search path does not have to be changed). Oh, great, we hardly need to do any work, Java.neturlclassloader is a classloader that meets our requirements. We can use it directly or inherit it.
This is the description of the two constructors of URLClassLoader in the doc of the j2se1.4 API:
URLClassLoader (url[] URLs)
Constructs a new urlclassloader for the specified URL using the default delegation parent ClassLoader.
URLClassLoader (url[] URLs, ClassLoader parent)
Constructs a new urlclassloader for the given URL.
Where url[] URLs are the class search paths we want to set, parent is the parent classloader of this classloader, and the default is System ClassLoader.
OK, now we can load class dynamically so that we can use the Newinstance method to get an object. But how do we shape this object. Can you shape this object into its own class?
First let's analyze the Java source file compilation, run it. The Javac command is compiled by calling the compile method of "Com.sun.tools.javac.Main" in "Java_home/lib/tools.jar":
public static int Compile (String as[]);
public static int Compile (String as[], printwriter printwriter);
A return of 0 indicates the success of the compilation and the string array as is the parameter we compile with the Javac command, separated by a space. For example:
Javac-classpath C:\foo\bar.jar; -D C:\ C:\Some.java
The string array as is {"-classpath", "C:\\foo\\bar.jar;.", "D", "c:\\", "C:\\some.java"}, and if you have the PrintWriter argument, The compiled information is then presented to the specified printwriter. The default output is System.err.
Where main is loaded by the JVM using the Launcher initialization system ClassLoader, according to the principle of overall responsibility, all the classes that the compiler relies on and references when parsing the Java source file will also be represented by system ClassLoader load, the compiler throws a "cannot resolve symbol" error if system ClassLoader cannot load a class.
So first compile will pass, that is, the compiler cannot compile a reference to the unknown class in classpath Java source files, and because of spelling mistakes or not put the required class library into the classpath, we must often see this "cannot resolve Symbol "This compilation error.
Second, we put this class into the compilation path, successfully compiled, and then in the runtime do not put it into the classpath and use our own ClassLoader to dynamically load this class, this time will also appear " Java.lang.NoClassDefFoundError, "What is the offence?"
Let's analyze again that the executable class that first calls this modeling statement must be loaded by the JVM using the Launcher initialization system ClassLoader, and according to the overall responsibility principle, when we sculpt, the JVM will also use system ClassLoader to try to load this class to sculpt an instance, which naturally throws a "java.lang.NoClassDefFoundError" violation when the system ClassLoader is not looking for this class.
OK, now let's conclude that the compilation of Java files and class loading execution, using the launcher initialized system ClassLoader as class loader, we cannot dynamically change system ClassLoader, Rather than letting the JVM use our own classloader to replace system ClassLoader, which limits compilation and runtime based on overall accountability, we cannot explicitly use a class that is not found by the system ClassLoader directly, That is, we can only use the Java Core Class library to extend class libraries and class libraries in classpath.
Not to forget. To try this again, we put this class into the classpath so that the system ClassLoader can recognize and load it. Then we load this class from the specified class file through our own ClassLoader (not able to delegate parent loading because it will be loaded by system ClassLoader from Classpath), and then instantiate an object and sculpt This class so that the JVM also recognizes this class (because system ClassLoader can locate and load this class from Classpath), and it does not load this class in Classpath, but from Classpath dynamic loading, so the head of the bar. Unfortunately, there will be "java.lang.ClassCastException" violations.
Why, then? Let's also analyze that, yes, although we have dynamically loaded this class from outside of Classpath using our own ClassLoader, the JVM will use System ClassLoader to load this class again when the instance is styled, And try to sculpt this class (another one) for system ClassLoader loaded with an instance of our own ClassLoader loaded class. Have you found any problems? That is, we try to sculpt an instance of class that is loaded from a classloader into another ClassLoader class, even though the names of these two classes are loaded from the same class file. Unfortunately, the JVM thinks this two class is different, that is, the JVM thinks different classloader load the same name class (even if it is loaded from the same class file). The reason for this is probably primarily for security reasons, so that all core Java classes are loaded by system ClassLoader, and we can't replace their instances with instances of the same name class that we loaded with our ClassLoader.
See here, smart readers must have thought of how to dynamically load our class, instantiate, sculpt and call it.
That is the use of one of the basic features of object-oriented polymorphism. Let's take the instance of our dynamically loaded class into a parent class that its system classloader can recognize. This is why. We'll have to analyze it again. When we use our own ClassLoader to dynamically load this we just take this class, we find that it has a parent class, the JVM first loads the parent class before loading it, the parent class is System ClassLoader can recognize, according to the delegation mechanism, it will be loaded by the system ClassLoader, and then our ClassLoader load this class, create an instance, modelling for this parent class, attention, shape into this parent class class, which is allowed by the object-oriented Java language and supported by the JVM, the JVM uses system ClassLoader to load the parent class again, and then sculpt this instance into the parent class class. You can see from this process that the parent class class is loaded by System ClassLoader, which is the same class that is loaded with the same class loader, so there is no exception when styling. According to the polymorphism, the method that invokes the parent class is actually executing the method that overrides the parent class method of this class (not the parent class). These methods can also refer to classes that are not recognized by system ClassLoader, because according to the overall responsibility principle, it is only possible to locate and load these classes as long as the classloader that is loaded into this class is our own defined ClassLoader.
This allows us to define a set of interfaces or base classes in advance and put them into classpath, and then dynamically load implementations or subclass of these interfaces or base classes at the time of execution. Don't you get it. Let's think about the servlet, the Web application server can load any class that inherits the servlet and execute it correctly, regardless of its actual class, or instantiate it as a servlet class, Then execute the servlet's init,dopost,doget and destroy, regardless of whether the servlet is from Web-inf/lib and web-inf/classes by system ClassLoader's Child ClassLoader (that is, custom ClassLoader) is dynamically loaded. So much hope that we all understand. This mechanism is used in containers such as APPLET,EJB.
For all the above situations, I hope you can actually write some example to experiment.
Finally, I'll say something else, ClassLoader. Although called the ClassLoader, it does not mean that it can only be used to load classes, we can also use it to obtain URLs for resources such as pictures, audio files, and, of course, these resources must be in the Jar class library or directory in Classpath. Let's look at the two ways to find resources and class for ClassLoader in the doc of the API:
Public URL getresource (String name)
Find resources with the specified name, a resource that can be accessed by class code to some extent depending on the location of the code (Pictures, audio, text, and so on).
The name of a resource is delimited by a '/' number that determines the path name of the resource.
This method first requests the parent ClassLoader to search for resources, and if there is no parent, searches the path of the ClassLoader (that is, Bootstrap ClassLoader) built into the virtual machine. If it fails, this method calls FindResource (String) to find the resource.
public static URL Getsystemresource (String name)
Finds a resource with the specified name from the search path used to load the class. This method uses the System class loader to locate the resource. That is, the equivalent of Classloader.getsystemclassloader (). GetResource (name).
For example:
System.out.println (Classloader.getsystemresource ("Java/lang/string.class"));
The result is:
Jar: File:/c:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/string.class
Indicates that the String.class file is in the Rt.jar Java/lang directory.
So we can package the pictures and other resources along with the class into the Jar class library (and, of course, we can package the resources separately) and add them to the class loader search path, we can not care about the specific location of these resources, let class loader to help us find.
Solution of Java Dynamic compile-time Com.sun.tools.javac.Main class loading problem under Ipse 2007-10-05 03:07 I saw eclipse. The path that the classpath loaded when the error was made is: G:\Program files\ Java\jdk1.5.0\jre\lib\ext\dnsns.jar
So I add Tools.jar to this G:\Program Files\java\jdk1.5.0\jre\lib\ext directory is also resolved, I do not know that there are other solutions.
=================
The following is the classpath when the report is loaded:
Classpath=c:\docume~1\jujumao\locals~1\temp\jetty__8081__openfans; G:\Program Files\java\jdk1.5.0\jre\lib\ext\dnsns.jar (the back of the skip)