17- parent class loader delegation mechanism instances depth analysis

Parent class loader delegation mechanism instances depth analysis

java.lang.ClassLoader
protected Class<?> loadClass(String name , 
                          boolean resolve) 
throws ClassNotFoundException

​ Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:

① Invoke findLoadedClass(String) to check if the class has already been loaded.
② Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
③ Invoke the findClass(String) method to find the class.

Using the binary name of the class specified load. The default implementation of this method to search for classes in the following order:

① call findLoadedClass (String) check whether the class is loaded.

② call the loadClass method on the parent class loader. If the parent is empty, then use the built-in virtual machine class loader.

③ call findClass (String) method to find the class.

​ If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object.

If the above steps to find the class, and the resolve flag is true, this method will call the class object generation resolveClass (Class) method.

​ Subclasses of ClassLoader are encouraged to override findClass(String), rather than this method.
​ Unless overridden, this method synchronizes on the result of getClassLoadingLock method during the entire class loading process.

Subclass encouraged loader rewrite findClass (String), instead of this method.

Unless rewritten, otherwise the whole class loading process, this method will result getClassLoadingLock synchronization method.

Parameters:
name - The binary name of the class
resolve - If true then resolve the class
Returns:
The resulting Class object
Throws:
ClassNotFoundException - If the class could not be found

Offer Source:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
  • Section 15 of the custom class loader continue to analyze:
    • In (1), (2), (3) were added at print statements, eventually found (1), (2) not being executed, only (3) is executed. Because MyTest16 loader1 = new MyTest16 ( "loader1 ") ;, is a constructor parameter, so that "the system class loader as parent class loader loader" , at the same time, and the system class loader can load MyTest16, so , the printout of the final statement is: the system class loader, rather than the class loader our custom.
    • The reason, according to the parent class loader delegation mechanism, MyTest16 ( "loader1") loader1 certainly not the first to try this class loader to load, but entrusted to his father tries to load, and loader1 father is the "system class loader . "
    • If a class loader loads successfully Test classes, then the class loader is referred defined class loader , all return Class successfully applied to the object class loader (including custom class loader) are called the initial class loader . [Here defined class loader is: the system class loader, class loader is initially: the system class loader and custom class loading (loader1)]
/**
 * 自定义类加载器
 */
public class MyTest16 extends ClassLoader{
    private String classLoaderName;       //标示性的属性,类加载器的名字
    private final String fileExtension = ".class";  //每一次中磁盘上读取的文件的扩展名
    public MyTest16(String classLoaderName){
        super();   //将系统类加载器当做该类加载器的父加载器
        this.classLoaderName = classLoaderName;
    }
    // 该方法的前提是已经有了parent这样一个加载器
    public MyTest16(ClassLoader parent, String classLoaderName){
        super(parent);   //显示执行该类加载器的父加载器(parent)
        this.classLoaderName = classLoaderName;
    }
    @Override
    public String toString() {
        return "[" + this.classLoaderName + "]";
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("findClass invoked:"+className);             //(1)
        System.out.println("class loader name:"+this.classLoaderName);	//(2)
       
        byte[] data = this.loadClassData(className);
        return this.defineClass(className,data,0,data.length); //最终返回一个字节class对象
    }

    /*
    	通过类的名字(className),把对应的文件的名字找到,并以输入输出流的形式最后返回一个字节数组,这个
    	字节数组就是从class文件中读取的二进制信息。
    */
    private byte[] loadClassData(String className){
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            this.classLoaderName = this.classLoaderName.replace(".","/");
            is = new FileInputStream(new File(className + this.fileExtension));
            int ch;
            while (-1 != (ch=is.read())){
                baos.write(ch);
            }
            data = baos.toByteArray();
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            try {
                baos.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return data;
    }

    public static void test(ClassLoader classLoader) throws Exception {
        //1.下面这个调用的loadClass方法,其实调用的还是ClassLoader类中的方法,
        //2.loadClass方法回去调用我们上面所重写的 findClass方法,根据二进制名字寻找
        //class对象(这里是MyTest10类)。 【这也是自定义类最关键的一环,重写findClass方法】
        Class<?> clazz = classLoader.loadClass("Jvm.MyTest10");
        Object object = clazz.newInstance();
        System.out.println(object);
        System.out.println(object.getClass().getClassLoader());  //(3)
    }

    public static void main(String[] args) throws Exception {
        MyTest16 loader1 = new MyTest16("loader1");
        test(loader1);
    }

}
运行结果:
      MyTest10 static block
      Jvm.MyTest10@6d6f6e28
      sun.misc.Launcher$AppClassLoader@18b4aac2

1. The use of a custom class loader to load the class files:

  • First MyTest10 class files, create a folder on the desktop folder Jvm put inside, and then delete the compiler-generated MyTest10.class file. When the program runs, loader1 loader (custom loader) first commissioned parents (the system class loader) to load the system class loader to obtain the corresponding class files to find classpath directories, however, because the class file is deleted, so loaded unsuccessful, then up until the parents find the root class loader, load or unsuccessful, then return to loader1 class loader to load the loader1 MyTest10.class
  • ★★ ★★ If the compiler generated MyTest10.class file is not deleted , then by the system class loader to load the class file, [reason: there are parents trust mechanism, loader1 class loader will call the parent class loader system to try to load classpath folder class file, this time because of the presence of class file in the classpath, so be loaded. And, not to load class files on the desktop]
/**
 * 自定义类加载器
 */
public class MyTest16 extends ClassLoader{
    private String classLoaderName;
    private String path;
    private final String fileExtension = ".class";
    public MyTest16(String classLoaderName){
        super();   //将系统类加载器当做该类加载器的父加载器
        this.classLoaderName = classLoaderName;
    }
    public MyTest16(ClassLoader parent, String classLoaderName){
        super(parent);   //显示执行该类加载器的父加载器(parent)
        this.classLoaderName = classLoaderName;
    }
    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public String toString() {
        return "[" + this.classLoaderName + "]";
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("findClass invoked:"+className);
        System.out.println("class loader name:"+this.classLoaderName);

        byte[] data = this.loadClassData(className);
        return this.defineClass(className,data,0,data.length); //最终返回一个class对象
    }

    private byte[] loadClassData(String className){
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        className = className.replace(".","\\");
        try {
            is = new FileInputStream(new File(this.path + className + this.fileExtension));
            baos = new ByteArrayOutputStream();
            int ch;
            while (-1 != (ch = is.read())){
                baos.write(ch);
            }
            data = baos.toByteArray();
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            try {
                is.close();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }
    public static void main(String[] args) throws Exception {
        MyTest16 loader1 = new MyTest16("loader1");
        loader1.setPath("C:\\Users\\admin\\Desktop\\");
		  //这里是将MyTest10的class文件,在桌面创建一个文件夹Jvm放里面,去加载桌面文件夹下的class文件
        Class<?> clazz = loader1.loadClass("Jvm.MyTest10");
        System.out.println("class:"+clazz.hashCode());
        Object object = clazz.newInstance();
        System.out.println(object);
    }
}
运行结果:
      findClass invoked:Jvm.MyTest10 <--- 说明调用了自定义的类加载器loader1去加载MyTest10.class
      class loader name:loader1		 <---
      class:1173230247
      MyTest10 static block
      Jvm.MyTest10@330bedb4
  • Example 2:

    • Premise: there is the classpath MyTest10.class file, run from the final results, creates two classes loaders, after the first class loader has been loaded MyTest.class, when a second load is the same class (MyTest. class), when it will not load because the source code should be judged, if the class is already loaded, then returned directly to the object. Therefore, the corresponding hashcode () returns the same, and MyTest10 static block is executed only once.

    • However, if MyTest10.class classpath the file is deleted, the operating results as shown below, but it is still MyTest10 class is loaded once, [ a class is only loaded once defined and has not been broken , because you can see MyTest10 class MyTest10 static block static block of code is executed only once, to be loaded only once described MyTest10], it can be seen that, hashcode class output () is different here involves namespace problem.

      • Namespaces:

        • Each class loader has its own namespace, the namespace by the class loader and all the parent class loader loaded classes .
        • In the same namespace, the two classes [full name of the class (including package of) the same does not occur].
        • In a different namespace, [there may be a full class name (including package of) the same] two classes.
      findClass invoked:Jvm.MyTest10
      class loader name:loader1
      class:1173230247
      MyTest10 static block   //第一加载,并初始化,静态代码块被执行力。
      Jvm.MyTest10@330bedb4
      
      findClass invoked:Jvm.MyTest10
      class loader name:loader2
      class:2125039532
      Jvm.MyTest10@12a3a380
      
//这里只写了主函数
public static void main(String[] args) throws Exception {
      MyTest16 loader1 = new MyTest16("loader1");
      loader1.setPath("C:\\Users\\admin\\Desktop\\");

      Class<?> clazz = loader1.loadClass("Jvm.MyTest10");
      System.out.println("class:"+clazz.hashCode());
      Object object = clazz.newInstance();
      System.out.println(object);

      System.out.println();

      MyTest16 loader2 = new MyTest16("loader2");
      loader1.setPath("C:\\Users\\admin\\Desktop\\");

      Class<?> clazz1 = loader2.loadClass("Jvm.MyTest10");
      System.out.println("class:"+clazz1.hashCode());
      Object object1 = clazz.newInstance();
      System.out.println(object1);
}
运行结果:
   class:1836019240            <--
   MyTest10 static block		 <--	
   Jvm.MyTest10@135fbaa4

   class:1836019240				 <--
   Jvm.MyTest10@45ee12a7
Published 25 original articles · won praise 0 · Views 1456

Guess you like

Origin blog.csdn.net/qq_40574305/article/details/104784864