自己動手寫 Java 虛擬機(二)-查找 Class 文件

相關文章:

我們都知道 Java 類加載有這麼幾個階段:加載、驗證、準備、解析和初始化。本文就是實現加載階段的查找 class 文件。《自己動手寫 Java 虛擬機》前兩章其實並未涉及到很多 JVM 相關的知識,主要是在爲後面做準備,一些內容書上過於繁瑣,不屬於主幹內容,這裏會簡寫或者忽略。

類路徑

按照搜索的先後順序,類路徑可以 分爲以下3個部分:

  • 啓動類路徑(bootstrap classpath)
  • 擴展類路徑(extension classpath)
  • 用戶類路徑(user classpath)

參考 java 命令,可以通過 -cp/-classpath 命令去指定目錄和 zip/jar 文件的類搜索路徑,從 Java6 開始支持通配符(*)。

增加命令參數

爲了方便,先在項目中增加了字符串操作的工具類:

/**
 * @author Dongguabai
 * @Description
 * @Date 創建於 2020-06-11 06:12
 */
public class StringUtils {

    public static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public static boolean isNotBlank(final CharSequence cs) {
        return !isBlank(cs);
    }

    private StringUtils(){}
}

DongguabaiJVMCommander 中定義一個內部類處理 -cp/-classpath 命令的參數:

    public static class ClassPathParams {
        @SubParameter(order = 0)
        public String classPath;

        @SubParameter(order = 1)
        public String classFile;

        public ClassPathParams() {
        }
    }

增加參數選項:

    /**
     * class load
     */
    @Parameter(names = {"-cp", "--classpath"}, required = false, arity = 2, description = "class search path of directories and zip/jar files")
    private ClassPathParams classPathParams;

查找 class 文件

這個其實也比較簡單,就是讀取一下 class 文件的內容。對於 Java 虛擬機來說,class 文件來源很多,這裏只實現從文件中獲取 class 文件。

定義一個工廠類:

/**
 * @author Dongguabai
 * @Description ClassLoader 工廠
 * @Date 創建於 2020-06-11 06:21
 */
public class ClassLoaderFactory {

    public static ClassLoader getClassLoader(String classPath){
        //todo 生成 ClassLoader
        return new DirClassLoader(classPath);
    }

    private ClassLoaderFactory() {
    }
}

定義 ClassLoader 抽象類:

/**
 * @author Dongguabai
 * @Description ClassLoader 抽象類
 * @Date 創建於 2020-06-11 06:21
 */
public abstract class ClassLoader {

    private String classPath;

    public abstract byte[] loadClass(String classFile);

    ClassLoader(String classPath) {
        this.classPath = classPath;
    }

    public String getClassPath() {
        return classPath;
    }
}

從目錄中加載 class:

/**
 * @author Dongguabai
 * @Description 解析目錄
 * @Date 創建於 2020-06-11 06:03
 */
public class DirClassLoader extends ClassLoader {

    public DirClassLoader(String classPath) {
        super(classPath);
    }

    @Override
    public byte[] loadClass(String classFile) {
        System.out.printf("DirClassLoader:[%s]\n", classFile);
        String filePath = getClassPath() + File.separator + classFile;
        try {
            return IOUtils.toByte(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

DongguabaiJVMCommander 中增加 loadClassPath 方法,輸出讀取到的 class 文件內容:

/**
 * @author Dongguabai
 * @Description
 * @Date 創建於 2020-06-08 13:36
 */
public class DongguabaiJVMCommander {

    private static final String CURRENT_VERSION = "Dongguabai jdk version \"0.0.1\"";

    /**
     * 查看版本
     */
    @Parameter(names = {"--version", "-v"}, required = false, arity = 0, description = "print product version and exit")
    private boolean version;

    /**
     * help
     */
    @Parameter(names = {"--help", "-h"}, required = false, arity = 0, description = " print the help message")
    private boolean help;

    /**
     * 運行 Java 代碼
     */
    @Parameter(names = {"--run"}, required = false, arity = 1, description = "to execute a java file")
    private String run;


    /**
     * class load
     */
    @Parameter(names = {"-cp", "--classpath"}, required = false, arity = 2, description = "class search path of directories and zip/jar files")
    private ClassPathParams classPathParams;

    public static void main(String... argv) {
        DongguabaiJVMCommander main = new DongguabaiJVMCommander();
        JCommander jCommander = JCommander.newBuilder()
                .addObject(main)
                .build();
        jCommander.parse(argv);
        main.run(jCommander);
    }

    /**
     * 運行
     * todo 優化
     *
     * @param jCommander
     */
    private void run(JCommander jCommander) {
        if (version) {
            System.out.println(CURRENT_VERSION);
            return;
        }
        if (help) {
            jCommander.usage();
            return;
        }
        if (classPathParams != null && StringUtils.isNotBlank(classPathParams.classFile) && StringUtils.isNotBlank(classPathParams.classPath)) {
            System.out.printf("load Class:%s  %s\n", classPathParams.classPath, classPathParams.classFile);
            loadClasspath();
        }
        if (StringUtils.isNotBlank(run)) {
            System.out.printf("execute:%s", run);
        }
    }

    private void loadClasspath() {
        ClassLoader classLoader = ClassLoaderFactory.getClassLoader(classPathParams.classPath);
        byte[] bytes = classLoader.loadClass(classPathParams.classFile);
        String s = CommonUtils.bytesToHexString(bytes);
        System.out.println(s);
    }

    public static class ClassPathParams {
        @SubParameter(order = 0)
        public String classPath;

        @SubParameter(order = 1)
        public String classFile;

        public ClassPathParams() {
        }
    }
}

測試

寫一個簡單的類:

/**
 * @author Dongguabai
 * @Description
 * @Date 創建於 2020-06-11 07:53
 */
public class UserV1 {
    
    private String username;
    
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

編譯後,增加啓動參數:

-cp /Users/dongguabai/IdeaProjects/dongguabai-jvm/target/classes/com/dongguabai/jvm/test UserV1.class

啓動:

load Class:/Users/dongguabai/IdeaProjects/dongguabai-jvm/target/classes/com/dongguabai/jvm/test  UserV1.class
DirClassLoader:[UserV1.class]
ca fe ba be 00 00 00 31 00 20 0a 00 05 00 1b 09 00 04 00 1c 09 00 04 00 1d 07 00 1e 07 00 1f 01 00 08 75 73 65 72 6e 61 6d 65 01 00 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 03 61 67 65 01 00 01 49 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 00 04 74 68 69 73 01 00 20 4c 63 6f 6d 2f 64 6f 6e 67 67 75 61 62 61 69 2f 6a 76 6d 2f 74 65 73 74 2f 55 73 65 72 56 31 3b 01 00 0b 67 65 74 55 73 65 72 6e 61 6d 65 01 00 14 28 29 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0b 73 65 74 55 73 65 72 6e 61 6d 65 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01 00 06 67 65 74 41 67 65 01 00 03 28 29 49 01 00 06 73 65 74 41 67 65 01 00 04 28 49 29 56 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 0b 55 73 65 72 56 31 2e 6a 61 76 61 0c 00 0a 00 0b 0c 00 06 00 07 0c 00 08 00 09 01 00 1e 63 6f 6d 2f 64 6f 6e 67 67 75 61 62 61 69 2f 6a 76 6d 2f 74 65 73 74 2f 55 73 65 72 56 31 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 00 21 00 04 00 05 00 00 00 02 00 02 00 06 00 07 00 00 00 02 00 08 00 09 00 00 00 05 00 01 00 0a 00 0b 00 01 00 0c 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 0d 00 00 00 06 00 01 00 00 00 08 00 0e 00 00 00 0c 00 01 00 00 00 05 00 0f 00 10 00 00 00 01 00 11 00 12 00 01 00 0c 00 00 00 2f 00 01 00 01 00 00 00 05 2a b4 00 02 b0 00 00 00 02 00 0d 00 00 00 06 00 01 00 00 00 0f 00 0e 00 00 00 0c 00 01 00 00 00 05 00 0f 00 10 00 00 00 01 00 13 00 14 00 01 00 0c 00 00 00 3e 00 02 00 02 00 00 00 06 2a 2b b5 00 02 b1 00 00 00 02 00 0d 00 00 00 0a 00 02 00 00 00 13 00 05 00 14 00 0e 00 00 00 16 00 02 00 00 00 06 00 0f 00 10 00 00 00 00 00 06 00 06 00 07 00 01 00 01 00 15 00 16 00 01 00 0c 00 00 00 2f 00 01 00 01 00 00 00 05 2a b4 00 03 ac 00 00 00 02 00 0d 00 00 00 06 00 01 00 00 00 17 00 0e 00 00 00 0c 00 01 00 00 00 05 00 0f 00 10 00 00 00 01 00 17 00 18 00 01 00 0c 00 00 00 3e 00 02 00 02 00 00 00 06 2a 1b b5 00 03 b1 00 00 00 02 00 0d 00 00 00 0a 00 02 00 00 00 1b 00 05 00 1c 00 0e 00 00 00 16 00 02 00 00 00 06 00 0f 00 10 00 00 00 00 00 06 00 08 00 09 00 01 00 01 00 19 00 00 00 02 00 1a 

輸出了這麼一堆,有木有很熟悉,下篇文章開始分析、解析 class 文件。

代碼地址:https://github.com/dongguabai/mini-jvm

References

  • 《自己動手寫 Java 虛擬機》
  • http://jcommander.org/#_variable_arities

歡迎關注公衆號
​​​​​​在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章