1. 程式人生 > >【深入淺出-JVM】(76):classloader

【深入淺出-JVM】(76):classloader

方法

  • public Class<?> loadClass(String name) throws ClassNotFoundException
    通過類名發揮這個類的Class例項
  • protected final Class<?> defineClass(byte[] b,int off,int len)
    根據給定的位元組碼流 b,off 和 len 引數表示實際的 class 資訊在byte 陣列中的位置和長度,其中 byte 陣列 b是 classloader 從外部獲取的
  • protected Class<?> findClass(String name)throws ClassNotFoundException
    檢視一個類
  • protected final Class<?> findLoadedClass(String name)

    分類

  • BootStrap ClassLoader
  • Extension ClassLoader
  • AppClassLoader

    啟動類載入器負責載入系統的核心類(rt.jar的java類),擴充套件類載入器載入 %JAVA_HOME/lib/ext/*.jar中的類,應用類載入器用於載入使用者類 (classpath),自定義類載入器載入一些特殊路徑的類(自定義classloader)

    雙親委託

  1. 當前 classloader 首先從自己已經載入的類中查詢是否此類已經載入,如果已經載入了則直接返回原來已經載入的類
  2. 當前 classloader 的快取中沒有找到被載入的類的時候,委託父類載入器去載入,父類載入器採用同樣的策略,首先查下自己的快取,然後委託父類去載入,一直到 bootstrap classloader
  3. 當所有的父類載入器都沒有載入的時候,再由當前的類載入器載入,並將其放入自己的快取中,下次請求的時候直接返回
  4. 一直迴圈重複

作用

  • 各個類載入器的基礎類統一

jar -cvf test.jar HelloLoader.class 把class打包成jar

Extension ClassLoader

例子:在 ext 路徑下放一個自己 jar 包並載入

package com.mousycoder.server;

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

idea 通過 structs->artifacts->jar 然後 build-> build artifacts->build 生成 helloworld.jar
放到 /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext

package com.mousycoder.mycode.thinking_in_jvm;

import java.lang.reflect.Method;

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-09-06 10:35
 */
public class ExtClassLoader {

    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(System.getProperty("java.ext.dirs"));
        Class<?> helloClass = Class.forName("com.mousycoder.server.HelloWorld");
        System.out.println(helloClass.getClassLoader());
    }
}

輸出

/Users/mousycoder/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
sun.misc.Launcher$ExtClassLoader@610455d6

可以看出是 ExtClassLoader 載入 java.ext.dirs 目錄

自定義類載入器

package com.mousycoder.mycode.thinking_in_jvm;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-09-06 11:13
 */
public class MyClassLoader extends ClassLoader {

    private final static Path DEFAULT_CLASS_PATH = Paths.get("","/Users/mousycoder/My");

    private final Path classDir;

    public MyClassLoader(){
        super();
        this.classDir = DEFAULT_CLASS_PATH;

    }

    public MyClassLoader(String classDir){
        super();
        this.classDir = Paths.get(classDir);
    }

    public MyClassLoader(String classDir, ClassLoader parent){
        super(parent);
        this.classDir = Paths.get(classDir);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] classBytes = this.readClassBytes(name);

            if (null == classBytes || 0 == classBytes.length){
                throw new ClassNotFoundException("can not load the class" + name);
            }
            return this.defineClass(name,classBytes,0,classBytes.length);
        } catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }


    private byte[] readClassBytes(String name) throws ClassNotFoundException, IOException {
        String classPath = name.replace(".","/");
        Path classFullPath = classDir.resolve( "HelloWorld1.class");
        if (!classFullPath.toFile().exists()){
            throw new ClassNotFoundException("The class" + name + "mpt found");
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){
            Files.copy(classFullPath,baos);
            return baos.toByteArray();
        } catch (IOException e){
            throw new ClassNotFoundException("load the class " + name + "occur error",e);
        }

    }


    @Override
    public String toString() {
        return "My ClassLoader";
    }
}

package com.mousycoder.mycode.thinking_in_jvm;

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-09-06 11:34
 */
public class MyClassLoaderTest {

    public static void main(String[] args) throws ClassNotFoundException {
        MyClassLoader classLoader = new MyClassLoader();
        Class<?> class1 = classLoader.loadClass("com.mousycoder.mycode.thinking_in_jvm.HelloWorld1");
        System.out.println(class1.getClassLoader());
    }
}
package com.mousycoder.mycode.thinking_in_jvm;

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-09-06 11:46
 */
public class HelloWorld1 {
    public static void main(String[] args) {
        System.out.println("Hello world1 ");
    }
}

把helloword1變成class放到/Users/mousycoder/My目錄下即可
輸出 My ClassLoader 代表 自定義類載入器載入了該類

上下文類載入器

作用

打破雙親委託機制,讓上層父類載入器可以使用子類的載入器載入物件,比如Spi中的介面類在系統載入器中,但是實現類在應用載入器中

Tomcat 類載入器

目的

  1. 保證每個應用的類庫獨立隔離(即使同限定名不同版本的)
  2. 保證相同類庫相同版本的類庫共享
  3. 保證容器自身的類庫和程式獨立

載入順序

  1. bootstrap 引導類載入器
  2. system 系統類載入器
  3. 應用類載入器 WEB-INF/classes
  4. 應用類載入器 WEB-INF/lib
  5. common 類載入器 CATALINA/lib