Home  >  Article  >  Java  >  Why write ClassLoader? Deep understanding of classloader in java

Why write ClassLoader? Deep understanding of classloader in java

php是最好的语言
php是最好的语言Original
2018-07-27 09:36:402104browse

What is ClassLoader? Among all programming languages, Java is unique in that it runs on the Java Virtual Machine. This means that the compiled program will run on the target machine in a unique, platform-independent form other than the target machine's format. This format is very different from traditional executable programs in many aspects.

Preface

Java ClassLoader is a crucial but often overlooked component in the Java operating system. It is responsible for finding and loading class files at runtime. Creating a custom ClassLoader can completely redefine how class files are loaded into the system.

This tutorial provides an overall overview of Java's ClassLoader and gives an example of a custom ClassLoader. This ClassLoader will automatically compile before loading the code. You'll learn what a ClassLoader does and how to create a custom ClassLoader.

This tutorial requires readers to have a basic understanding of Java programming, including creating, compiling, and executing simple command-line Java programs.

After reading this tutorial, you will know how to:

  • Extend the functionality of the JVM

  • Create a custom ClassLoader

  • Learn how to integrate ClassLoader into Java applications

  • Modify ClassLoader to conform to the Java2 version

What is ClassLoader

Among all programming languages, Java is unique in that it runs on the Java Virtual Machine. This means that the compiled program will run on the target machine in a unique, platform-independent form other than the target machine's format. This format is very different from traditional executable programs in many aspects.

The biggest difference between a Java program and a C or C program is that it is not a single executable file, but consists of many separate class files, each class file corresponding to a Java class.

Not only that, these class files are not loaded into memory at once, but on demand. ClassLoader is the part of JVM that loads classes into memory.

In addition, Java ClassLoader is written in Java. This means that you can easily create your own ClassLoader without knowing more details about the JVM.

Why write ClassLoader

If the JVM already has a ClassLoader, why do you need to write another one? Good question, the default ClassLoader only knows how to load class files from the local file system. In general scenarios, when you write code locally and compile it locally, it is completely sufficient.

However, one of the most novel features of the JAVA language is that classes can be obtained from the local hard drive or outside the Internet. For example, the browser uses a custom ClassLoader to obtain executable content from the website.

There are many other ways to obtain class files. In addition to loading class files locally or from the Internet, you can also use a class loader to:

  • Automatically verify digital signatures before executing untrusted code

  • Transparent decryption code using password provided by user

  • Create custom dynamic classes based on user’s specific needs

Any generation The content of Java bytecode can be integrated into your application.

Example of custom ClassLoader

If you have ever used an applet, you must have used a custom class loader.

When Sun released the Java language, one of the most exciting things was watching how the technology performed timely loading of code from a remote Web server. The exciting thing is that they send bytecode over an HTTP connection from a remote web server and run it locally.

The Java language's ability to support custom ClassLoader makes this idea possible. There is a custom ClassLoader in the applet. It does not load the class file from the local file system, but obtains it from the remote Web server, loads the original bytecode through Http, and then converts it into a class in the jvm.

Class loaders in browsers and Applets also have other functions: security management, preventing applets on different pages from affecting each other, etc.

Next we will create a custom class loader called CompilingClassLoader(CCL). CCL will help us compile Java code. It's basically like building a simple make program directly into the running system.

ClassLoader structure

The basic purpose of ClassLoader is to provide services for class requests. The JVM needs a class, so it asks the ClassLoader to load the class by its name. ClassLoader attempts to return an object representing the class.

A custom ClassLoader can be created by overriding the methods corresponding to different stages of this process.

In the remainder of this article, you will learn about some key methods in ClassLoader. You'll learn what each method does and how it's called during class loading. You'll also learn what needs to be done when you customize a ClassLoader.

loadClassMethod##,

ClassLoader.loadClass() method is the entrance to ClassLoader. Its method tag is as follows:

Class loadClass(String name, boolean resolve)

nameThe parameter represents the name of the class required by the JVM, such as Foo or java.lang.Object .

resolveThe parameter indicates whether the class needs to be resolved. Class parsing can be understood as completely preparing the class for execution. Parsing is not necessary. If the JVM only needs to determine the existence of the class or find out its parent class, there is no need to parse.

Before the java1.1 version, custom ClassLoader only needed to override the loadClass method.

defineClass method

defineClass method is the core of the entire ClassLoader. This method converts the original byte array into a Class object. The raw byte array contains data obtained locally or remotely.

defineClass is responsible for handling many complex, mysterious and implementation-dependent parts of the JVM. It parses the bytecode into runtime data structures, checks their validity, etc. Don't worry, you don't have to implement this yourself. In fact, you can't override it at all because the method is final.

findSystemClass method

findSysetmClass method loads files from the local file system. It looks for a class file in the local file system and, if present, converts it from raw bytes to a class object using defineClass. This is the JVM's default mechanism for loading classes when running a Java application.

For custom ClassLoader, we will only call the findSystemClass method after trying other methods to load the class content. The reason is simple: a custom ClassLoader contains some steps for loading special classes, but not all classes are special classes. For example, even if the ClassLoader needs to get some classes from the remote website, there are still many classes that need to be loaded from the local Java library. These classes are not our focus, so we need the JVM to obtain them in the default way.

The entire process is as follows:

  • Request a custom ClassLoader to load a class

  • Check whether the remote server has the class

  • If there is, get and return

  • If not, we assume that the class is a local base class and call findSystemClass Load from the file system.

In most custom ClassLoaders, you need to first use findSystemClass to reduce access to the remote website, because most Java classes are located locally in the class library. However, as you'll see in the next section, we don't want the JVM to load classes from the local file system before automatically compiling the application code.

resolveClassMethod

As mentioned above, class loading can be done partially (without parsing) or completely (with parsing). When we implement our own loadClass method, we may need to call the resolveClass method, depending on the value of the resolve parameter in loadClass .

findLoadedClassmethod

findLoadedClass method acts as a caching calling mechanism: when the loadClass method is called, he will call This method is used to check whether the class has been loaded, eliminating the need for repeated loading. This method should be called first.

Integrate it

In our exampleloadClassperform the following steps (here we will not pay special attention to what magical method is used to obtain the class file. It can It is obtained from local, network or compressed files. In short, we obtain the bytecode of the original class file):

  • Call findLoadedClass to check whether it has been loaded Pass the class

  • If not, use magic to get the raw bytecode

  • If get the bytecode, call defineClassConvert it into a Class object

  • If the bytecode is not obtained, call findSystemClass to see if it can be obtained from The local file system obtains the class

  • If the resolve value is true, resolveClass is called to resolve the Class object

  • If the class is still not found, throw ClassNotFoundException

  • Otherwise, return the class to the caller

CompilingClassLoader

The role of CCL is to ensure that the code has been compiled and is the latest version.
The following is a description of this class:

  • When a class is needed, check whether the class is on disk, in the current directory or the corresponding subdirectory

  • If the class does not exist, but its source code exists, call the Java compiler to generate the class file

  • If the class file exists, check whether it is The version of the source code is old. If it is lower than the version of the source code, regenerate the class file

  • If the compilation fails, or other reasons prevent the class file from being generated from the source code, throw ClassNotFoundException

  • If there is still no class file, then it may be in some other libraries, call findSystemClass to see if it is useful

  • If the class is still not found, throw ClassNotFoundException

  • Otherwise, return the class

Java是如何编译的

在深入研究之前,我们应该回过头来看一下Java的编译机制。总的来说,当你请求一个类的时候,Java不只是编译各种类信息,它还编译了别的相关联的类。

CCL会按需一个接一个的编译相关的类。但是,当CCL编译完一个类之后试着去编译其它相关类的时候会发现,其它的类已经编译完成了。为什么呢?Java编译器遵循一个规则:如果一个类不存在,或者它相对于源码已经过时了,就需要编译它。从本质上讲,Java编译器先CCL一步完成了大部分的工作。

CCL在编译类的时候会打印其编译的应用程序。在大多数场景里面,你会看到它在程序的主类上调用编译器。

但是,有一种情况是不会在第一次调用时编译所有类的的。如果你通过类名Class.forNasme加载一个类,Java编译器不知道该类需要哪些信息。在这种场景下,你会看到CCL会再次运行Java编译器。

如何使用CompilingClassLoader

为了使用CCL,我们需要用一种独特的方式启动程序。正常的启动程序如下:

% java Foo arg1 arg2

而我们启动方式如下:

% java CCLRun Foo arg1 arg2

CCLRun是一个特殊的桩程序,它会创建一个CompilingClassLoader并使用它来加载程序的main方法,确保整个程序的类会通过CompilingClassLoader加载。CCLRun使用Java反射API来调用main方法并传参

Java2中ClassLoader的变化

Java1.2以后ClassLoader有一些变动。原有版本的ClassLoader还是兼容的,而且在新版本下开发ClassLoader更容易了

新的版本下采用了delegate模型。ClassLoader可以将类的请求委托给父类。默认的实现会先调用父类的实现,在自己加载。但是这种模式是可以改变的。所有的ClassLoader的根节点是系统ClassLoader。它默认会从文件系统中加载类。

loadClass默认实现

一个自定义的loadClass方法通常会尝试用各种方法来获得一个类的信息。如果你写了大量的ClassLoader,你会发现基本上是在重复写复杂而变化不大的代码。

java1.2的loadClass的默认实现中允许你直接重写findClass方法,loadClass将会在合适的时候调用该方法。

这种方式的好处在于你无须重写loadClass方法。

新方法:findClass

该方法会被loadClass的默认实现调用。findClass是为了包含ClassLoader所有特定的代码,而无需写大量重负的其他代码

新方法:getSystenClassLoader

无论你是否重写了findClass或是loadClass方法,getSystemClassLoader允许你直接获得系统的ClassLoader(而不是隐式的用findSystemClass获得)

新方法:getParent

该方法允许类加载器获取其父类加载器,从而将请求委托给它。当你自定义的加载器无法找到类时,可以使用该方法。父类加载器是指包含创建该类加载代码的加载器。

源码

// $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().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 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;
  }
}
import java.lang.reflect.*;
 
/*
 
CCLRun executes a Java program by loading it through a
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 into
    // 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 to
    // 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 );
  }
}
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] );
  }
}
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();
    }
  }
}
 package baz;
 
public class Baz
{
  public Baz( String a, String b ) {
    System.out.println( "baz! "+a+" "+b );
  }
}
public class Boo
{
  public Boo() {
    System.out.println( "Boo!" );
  }
}

相关文章:

基于Java类的加载方式之classloader类加载器详解

Java虚拟机学习 - 类加载器(ClassLoader)

相关视频:

全面解析Java注解

The above is the detailed content of Why write ClassLoader? Deep understanding of classloader in java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn