Class Loading --- (class load mechanism, the story of developers have to know)

xiaoxiao2021-03-06  39

Maybe you think Class Load is a high-level topic, no matter what, you still have to know it as a developer.

This article is based on the latest JDK5, but the content of the complaint contains the most basic principles, I hope you can learn more about your own language.

Understand ClassLoader

If you define an org.test.object.

You write this in the program:

Import Ort.Test.Object

Object o = new string (); Maybe you are pleasant to write no problem, but actually you are wrong.

This will report this

ClassCastException

,

A class is determined by it is determined by its Package and class name (that is, its namespace).

Org.test.Object is not equivalent to java.lang.object

In Java, each class is a java.lang.class instance. All classes can be customized like this: java.lang.class klass = myclass.class;

Classification, you can be: Myclass myclass = new myclass () can also be: myclass.newinstance ();

In JVM. All obtained

Java.lang.classloader

And its subclass is loaded, we must start from Java_Home / JRE / RT.ja when running the program. However, we have found that there is no introduction to bootstrap.jar in the JDK document. In fact, bootstrap is outside JDK, it has a way and JVM is different. In addition to ClassLoader in JDK, there are also the following.

Java.net.urlclassloader java.security.secureclassloader java.rmi.server.rmiclassLoader Sun.Applet.AppletClassLoader

Class Loaders working principle

In addition to Bootstrap, all ClassLoader has a parent class loader, they are all instanceof java.lang.classloader

Take a look at an example in JDK1.5

If there is a method loadingClass

protected synchronized Class loadClass (String name, boolean resolve) throws ClassNotFoundException {// First check if the class is already loaded Class c = findLoadedClass (name) ; if (c == null) {try {if (parent =! NULL) {c = parent.loadClass (name, false);} else {c = findbootstrapClass0 (Name);}} catch (classnotfoundexcection e) {// if still not found, then invoke // Findclass to find the class. c = FindClass (Name);}}}} {resolveclass (c);} return c;}

There are two ways to set it the parent class. Public class myclassloader extends classloader {public myclassloader () {super (myclassloader.class.getClassLoader ());}}

or

Public class myclassloader extends classloader {public myclassloader () {super (getclass (). getClassLoader ());}}

Here is the first choice.

Because getClass () is a method within the constructor, there must be a constructor code existence, but if not, find the parent class ClassLoader, until finding FindbootstrapClass, if it does not exist At that time, the FindClass () method was executed. . (That will report a classNotFoundException.

Let's take a look at Findclass ()

Protected class FindClass (String Name) throws classnotfoundexception {throw new classnotfoundexception (name);

In the FindClass () method, the Class Loader is to get the domain code (which is the content of the * .class file), it is not necessarily .class file, these byte codes can come from local or system, network (By this you can understand COBRA, RMI), or use

BCEL (Apache is based on the byte code to get a engine library) ... and more. One, bytecode found. At that time, I started executing the defineClass () method. At that time, ClassLoader defined a class. Each classLoader has different classes. If there are two ClassLoader load two identical programs, defineClass () defines two classes. Please see (Java Language Specification)

There is a picture showing a MyMainClass.class how to load execution. (Load the same target.class by multiple ClassLoad), --- According to the above, since the two classLoader () loaded by Target.class, the defineClass () will generate two Class definition.

Therefore, it is easy to draw the following conclusions: Target Target1 = (target) Target2; is incorrect. Target1 and target2 are defined by two different ClassLoader.

Please see

INSIDE CLASS Loaders

(Andreas Schaefer)

Do we need custom ClassLoader?

One reason: If we customize ClassLoader, we can control the JVM loading action. The above says that a class logo is due to the Package ClassName. For all classes that implement the Java.io.Serializable interface, these types are managed by SerialVersionuid (RMI, JNDI, and Security have such IDs). It expressed 64-bit HASH (this hash consists of ClassName, File, Method). Technically, if ClassName, Field, Mehtod is the same, it will be considered as the same version. Suppose there is such a case, we have to write a Java execution engine (such as: Publish a Server end program with a RMI, execute the client interface method), since it is necessary to execute, the engine must implement the interface with the client specific task ( Here is Taskintf). One, the task is submitted to the execution engine, the first thing for Server is to load all the code to be executed. Suppose different terminals are submitted to different code. And all the same package name, and the same class name. Can the server discriminates the execution request submitted by that client?

Now there is a problem: if the server is executed in one execution program to submit the same version of the code, how to make the client get expected results?

Don't think this is very simple, build a RMI to play first below. See what the result will be.

The local file is shown below.

Figure 2 Program directory structure (including code in this article).

In the Samepath directory, there are two version.version.class, they have the same name class name, the only difference is.

The method in the V1 directory is:

Public void fx () {log ("this =" this "; version.fx (1).");}

The method in the V2 directory is:

Public void fx () {log ("this =" this "; version.fx (2).");}

Execute a look:

Set classpath =.;% current_root% / v1;% current_root% / v2% java_home% / bin / java test results as follows

Figure 3. ClassPath's catalog is set to V1

Switch to

Set classpath =.;% current_root% / v2;% current_root% / v1% java_home% / bin / java test results as follows:

Figure 4. ClassPath directory is set to V2

Obviously, the above example can be found in ClassPath. If we put V1, V2's Version.Version. Customized. And put them into a MyExtension.jar package, put it in a java.ext.dirs directory. . At this time, it is loaded via EXTCLASSLOADER, not AppClassLoader.

The result will be as follows:

Figure 5. AppClassLoader and ExtClassLoader

Note Sun.Misc.launcher $EXTClassLoader@a9c85c This shows that EXTCLASSLOADER is loaded.

Continue to look down, another example. An example in the DIFFERENTVERSIONS directory, contains an execution engine such as the RMI ServerIMPL. The Client implemented a Common.TaskIntf interface. Two client.taskimpls are as follows: Static {log ("Client.taskimpl.class.getClassLoader (V1):" Taskimpl.class.getClassLoader ());} public void execute () {log ("this =" THIS "; execute (1)");

The other is:

Static {log ("Client.taskimpl.class.getClassLoader (V1):" TaskImpl.class.getClassLoader ());} public void execute () {log ("this =" this "; Execute (2)" }

This is to be executed (the order is casual, where% current_root% / client2 is placed in front):

ClassPath =% current_root% / common;% current_root% / server;% current_root% / client2;% current_root% / client1% java_home% / bin / java server.server

Start Server ..

Submit two Client to the server, (even if the client is written in the program, the client is shown, respectively, as shown in Figure 6.)

Figure 6. Execution Engine Server Console

Let's look at the following two figures (Figures 7 and 8), respectively, the Client end is executed.

Figure 7. Execution Engine Client 1 Console

Figure 8. Execution Engine Client 2 Console

Throughout the above three execution results, it is found that because the server is started to use AppClassLoader. So, no matter how it is loaded, it is client2 (because Client2 ClassPath is more in the order),

Here, the Client1 is very depressed, it performs clearly that EXECUTE (1) is sent to the server side through RMI to execute (2) ..

It is worth noting that after the client1, Client2 is sent to the server execution, the server-side record is: client.taskimpl.class.getClassLoader (V2): sun.misc.lanchor @ AppClassLoader @ xxxx zhiz only executed once. And this=Client.taskimpl@xxxxx execute (2); executed twice

It has already been said, and for a ClassLoader, the same page classname can only define a class, and different ClassLoad will define different Class if loading the same page.classname.

Here, I found out that it seems not easy to solve the problem. (.

How do you solve it? The answer is --- use custom ClassLoader .. If you wait, please see (the code in DifferentVersionsPush in the catalog)

Obviously, we must have a custom ClassLoader.

How to construct a custom ClassLoader Since the custom ClassLoader can solve the above problem, then take a look, how we use custom ClassLoader.

Combined with the original code of this variety - (in DifferentVersionsPush's directory), there is a FileSystemClassLoader, the class diagram is described below:

Figure 9.

Look at his method FindclassBytes (String classname);

public byte [] findClassBytes (String className) {try {String pathName = currentRoot File.separatorChar className replace (, File.separatorChar '.') ".class";. FileInputStream inFile = new FileInputStream (pathName); byte [ ] classBytes = new byte [inFile.available ()]; inFile.read (classBytes); return classBytes;} catch (java.io.IOException ioEx) {return null;}} public Class findClass (String name) throws ClassNotFoundException {byte [] classBytes = findClassBytes (name); if (classBytes == null) {throw new ClassNotFoundException ();} else {return defineClass (name, classBytes, 0, classBytes.length);}} public Class findClass (String name, byte [] Classbytes) throws classnotfoundexception {if (ClassBytes == null) {THR ow new ClassNotFoundException ( "(classBytes == null)");} else {return defineClass (name, classBytes, 0, classBytes.length);}} public void execute (String codeName, byte [] code) {Class klass = null Try {klass = Findclass (CODENAME, CODE); Taskintf Task = (Taskintf) Klass.newInstance (); task.execute ();} catch (exception exception) {exception.printStackTrace ();}}

This class FileSystemClassLoader is used by the client to define the Class and convert the client.taskimpl (v1) to Byte [], and then byte [] is then sent to the RMI Server execution. (Telling DefineClass () can perform any bytecode, from the compiled file, the network is even the BCEL bytecode engine, in the Server side, but also define the formiTsystemClassLoader to define the form of CLIENT.TASKIMPL in the form of Byte []. . Please see the CLIENT code:

public class Client {public static void main (String [] args) {try {byte [] code = getClassDefinition ( "client.TaskImpl"); serverIntf.execute ( "client.TaskImpl", code);} catch (RemoteException remoteException) {remoteException.printStackTrace ();}} private static byte [] getClassDefinition (String codeName) {String userDir = System.getProperties () getProperty ( "BytePath");. FileSystemClassLoader fscl1 = null; try {fscl1 = new FileSystemClassLoader (userDir) } Catch (filenotfoundexception) {filenotfoundexception.printStackTrace ();} returnifl1.findclassbytes (codename);}}

In the RMI server side serverImpl program, bytecode (byte []) is accepted (byte []), and FileSystemClassLoader will construct a Class, real example, and execute from byte []. One thing to pay attention to: Every time a client is received, FileSystemClassLoad is re-instantified (you can see in the execution result), which means that Client.Impl is not found in the classpath, but through FileSystemClassLoader. FindClass () to perform defineClass (), so each FileSystemClassLoader is created a new instance, natural define Class is also different. In this way, we can distinguish these two classes in the execution of RMI. (Client.taskimpl! = Client.taskimp has been concluded.)

Take a look at the execution code of the server:

public void execute (String codeName, byte [] code) throws RemoteException {FileSystemClassLoader fileSystemClassLoader = null; try {fileSystemClassLoader = new FileSystemClassLoader (); fileSystemClassLoader.execute (codeName, code);} catch (Exception exception) {throw new RemoteException (exception .getMessage ());}} The execution result of the server side:

Figure 10, server side display

The following two figures are displayed by the client.

Figure 11. Execution display of Client1

Figure 12. CLIENT2 execution result

Ha, there are so many sprinkles on it. It is a step in step by step. How do you perform a "different version" code in the same VM virtual machine. (These code has the same class name and package name).

Class Loaders is applied in J2EE.

Here you are not enough to have something you are now. . . My A_WAR.WAR's Web Project is com.mycom.test and I am in another B_war.war's wenb project is also com.mycom.test and they still work well. When a large EJB project, a server deploying multiple EJBs, WAR project, they will not affect each other. AppServer will have its own loading strategy, such as the JAR package in your web, which will take precedence over the Appserver itself.

In addition, J2EE's ClassLoader mechanism can be able to refer to this article on TSS.

Understanding J2ee Application Server Class Loading Architectures

" resource:

Sample code for this article JDK 1.5 API Docs The Java language specification "Understanding Extension Class Loading" in the Java tutorial "Inside Class Loaders" from ONJava "Inside Class Loaders: Debugging" from ONJava "? What version is your Java code" from JavaWorld "Understanding J2EE Application Server Class Loading Architectures" from theserverside byte code engineering library server-based java programming by TED Neward

Original: http://www.onjava.com/pub/a/onjava/2005/01/26/ClassLoading.htm

转载请注明原文地址:https://www.9cbs.com/read-63239.html

New Post(0)