1. 程式人生 > >《自己動手寫java虛擬機器》學習筆記(四)-----搜尋class檔案(java)

《自己動手寫java虛擬機器》學習筆記(四)-----搜尋class檔案(java)

 

專案地址:https://github.com/gongxianshengjiadexiaohuihui

首先是定義一個抽象類,把四種路徑的格式抽象出來

Entry.java

package classpath;

import java.io.IOException;

/**
 * @ClassName Entry
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/12 15:31
 * @Version 1.0
 */
public abstract class Entry {
    /**
     * 路徑分隔符,windows用; liunx/unix 用 :
     */
    public static final String PATHLISTSEPARATOR = System.getProperty("os.name").contains("Windows") ? ";" : ":" ;

    /**
     * 負責尋找和載入class檔案
     * @param className
     * @return
     * @throws IOException
     */
    public abstract byte[] readClass(String className) throws IOException;

    /**
     *
     * @return 返回路徑的字串形式
     */
    public abstract String printAbsPath();

    /**
     * 工廠方法,根據傳入路徑的格式返回不同格式路徑的實體類
     * @param path
     * @return
     */
    public static Entry newEntry(String path){
        if (path.contains(PATHLISTSEPARATOR)){
            return new CompositeEntry(path);
        }
        if(path.contains("*")){
            return new WildcardEntry(path);
        }
        if(path.contains(".jar") || path.contains(".JAR") || path.contains(".zip") || path.contains(".ZIP")){
            return new ZipJarEntry(path);
        }
        return new DirEntry(path);
    }

}

然後寫具體實現

DirEntry.java

package classpath;

import java.io.*;

/**
 * @ClassName DirEntry
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/12 15:50
 * @Version 1.0
 */
public class DirEntry extends Entry {
    private String absDir;

    public DirEntry(String path){
        File dir = new File(path);
        if(dir.exists()){
            absDir=dir.getAbsolutePath();
        }
    }
    /**
     * 負責尋找和載入class檔案
     *
     * @param className
     * @return
     * @throws IOException
     */
    @Override
    public byte[] readClass(String className) throws IOException {
        File file = new File(absDir,className);
        byte[] temp = new byte[100];
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
        ByteArrayOutputStream out = new ByteArrayOutputStream((int)file.length());
        int length=0;
        while((length=in.read(temp))!=-1){
            out.write(temp,0,length);
        }
        if(in != null ){
            in.close();
        }
        if(out != null){
            out.close();
        }
        return out.toByteArray();
    }

    /**
     * @return 返回路徑的字串形式
     */
    @Override
    public String printAbsPath() {
        return absDir;
    }
}

ZipJarEntry.java

package classpath;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @ClassName ZipJarEntry
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/12 16:22
 * @Version 1.0
 */
public class ZipJarEntry extends Entry {

    private String absPath;
    private String zipName;

    public ZipJarEntry(String path){
        File file = new File(path);
        if(file.exists()){
            absPath = file.getAbsolutePath();
            zipName = file.getName();
            //去掉結尾的.jar或.zip
            zipName = zipName.substring(0,zipName.length() - 4 );
        }
    }
    /**
     * 負責尋找和載入class檔案
     *
     * @param className
     * @return
     * @throws IOException
     */
    @Override
    public byte[] readClass(String className) throws IOException {
        File file = new File (absPath);
        ZipFile zf =new ZipFile(file);
        ZipEntry zipEntry=null;
        /**
         * 根據字尾不同獲取實體類
         */
        if(absPath.contains(".zip")||absPath.contains(".ZIP")){
            zipEntry = zf.getEntry(zipName+"/"+className);
        }else{
            zipEntry = zf.getEntry(className);
        }
        if(zipEntry == null){
            return null;
        }
        BufferedInputStream in = new BufferedInputStream(zf.getInputStream(zipEntry));
        ByteArrayOutputStream out = new ByteArrayOutputStream((int)zipEntry.getSize());
        int length = 0;
        byte[] temp = new byte[1024];
        while((length = in.read(temp)) != -1){
            out.write(temp,0,length);
        }
        if(in != null){
            in.close();
        }
        if(out != null){
            out.close();
        }
        return out.toByteArray();
    }

    /**
     * @return 返回路徑的字串形式
     */
    @Override
    public String printAbsPath() {
        return absPath;
    }
}

 

CompositeEntry.java

package classpath;

import java.io.IOException;
import java.util.ArrayList;

/**
 * @ClassName CompositeEntry
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/12 17:04
 * @Version 1.0
 */
public class CompositeEntry extends Entry {
    private String pathList;
    public  ArrayList<Entry> compositeEntry;
    public CompositeEntry(){}
    public CompositeEntry(String path){
        this.pathList = path;
        String[] paths = pathList.split(Entry.PATHLISTSEPARATOR);
        compositeEntry = new ArrayList<Entry>(paths.length);
        for(int i = 0; i < paths.length; i++){
            compositeEntry.add(newEntry(paths[i]));
        }
    }
    /**
     * 負責尋找和載入class檔案
     *
     * @param className
     * @return
     * @throws IOException
     */
    @Override
    public byte[] readClass(String className) throws IOException {
        byte[] data;
        for(Entry entry:compositeEntry){
            data =entry.readClass(className);
            if(data != null){
                return data;
            }
        }
        return null;
    }

    /**
     * @return 返回路徑的字串形式
     */
    @Override
    public String printAbsPath() {
        return pathList;
    }
}

WildcardEntry.java

package classpath;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

/**
 * @ClassName WildcardEntry
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/12 17:17
 * @Version 1.0
 */
public class WildcardEntry extends Entry {

    private CompositeEntry compositeEntry;

    private String path;
    public WildcardEntry(String path){
        this.path=path;
        /**
         * 去掉*
         */
        String baseDir = path.substring(0,path.length()-1);
        File dir = new File(baseDir);
        File[] files = dir.listFiles();
        compositeEntry = new CompositeEntry();
        compositeEntry.compositeEntry=new ArrayList<Entry>();
        for(File file : files){
            if((file.isFile()) && (file.getName().endsWith(".jar") || file.getName().endsWith(".JAR"))){
                compositeEntry.compositeEntry.add(new ZipJarEntry(baseDir+"/"+file.getName()));
            }
        }
    }
    /**
     * 負責尋找和載入class檔案
     *
     * @param className
     * @return
     * @throws IOException
     */
    @Override
    public byte[] readClass(String className) throws IOException {
        return compositeEntry.readClass(className);
    }

    /**
     * @return 返回路徑的字串形式
     */
    @Override
    public String printAbsPath() {
        return path ;
    }
}

四種實現寫好了,然後寫classpath.java去封裝上面四種格式的路徑,成為對外的唯一介面

package classpath;

import java.io.File;
import java.io.IOException;

/**
 * @ClassName ClassPath
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/13 9:07
 * @Version 1.0
 */
public class ClassPath {
    /**
     * 啟動類路徑
     */
    private Entry bootstrapClasspath;
    /**
     * 擴充套件類路徑
     */
    private Entry extClasspath;
    /**
     * 使用者類路徑
     */
    private Entry userClasspath;

    public ClassPath(String jreOption,String cpOption){
        parseClasspath(jreOption,cpOption);
    }


    private void parseClasspath(String jreOption,String cpOption){
        String jreDir = getJreDir(jreOption);
        /**
         * 啟動類路徑
         */
        String jreLibPath = jreDir + "\\" + "lib" + "\\" + "*";
        bootstrapClasspath = new WildcardEntry(jreLibPath);
        /**
         * 擴充套件類路徑
         */
        String jreExtPath = jreDir + "\\"+ "lib" + "\\" + "ext" +"\\"+"*";
        extClasspath = new WildcardEntry(jreExtPath);
        /**
         * 使用者類路徑
         */
        userClasspath = Entry.newEntry(cpOption);
    }

    /**
     * //1優先使用使用者輸入的-Xjre選項作為jre目錄
     * 	//2當前目錄下尋找jre目錄
     * 	//3用JAVA_HOME環境變數
     * @param jreOption
     * @return
     */
    private String getJreDir(String jreOption){
        File file;
        /**
         * 第一種情況
         */
        if(jreOption != null && !"".equals(jreOption)){
            file = new File(jreOption);
            if(file.exists()){
                return jreOption;
            }
        }
        /**
         * 第二種情況
         */
        file = new File("jre");
        if(file.exists()){
            return file.getAbsolutePath();
        }
        /**
         * 第三種情況
         */
        if(System.getenv("JAVA_HOME") != null){
            return System.getenv("JAVA_HOME")+"\\"+"jre";
        }
        /**
         * 三種情況都不滿足,丟擲異常
         */
        throw new RuntimeException("can not find jre folder!");
    }

    public byte[] readClass(String className){
        /**
         * 統一規範格式
         */
        if(className.endsWith(".class")){
            throw new RuntimeException("TypeError:" + className + "should be" + className.substring(0,className.length()-6));
        }
        className = className.replace(".","/") + ".class";
        byte[] data;
        try{
            data = bootstrapClasspath.readClass(className);
            if(data != null){
                return data;
            }
            data = extClasspath.readClass(className);
            if(data != null){
                return data;
            }
            data = userClasspath.readClass(className);
            if(data != null){
                return data;
            }
        }catch (IOException e){
            e.printStackTrace();
        }

        throw new RuntimeException("can't find class");

    }
    public String printAbsPath(){
        return userClasspath.printAbsPath();
    }
}

修改Main.java

import classpath.ClassPath;

import java.util.Arrays;

/**
 * @ClassName Main
 * @Description TODO
 * @Author Mr.G
 * @Date 2018/10/9 10:43
 * @Version 1.0
 */
public class Main {
    public static void main(String[] args){
        Cmd cmd=new Cmd(args);
        if(!cmd.isRightFmt||cmd.helpFlag){
            cmd.printUsage();
        }else if(cmd.versionFlag){
            System.out.println("version 0.0.1");
        }else{
            startJVM(cmd);
        }
    }
    public static void startJVM(Cmd cmd){
        ClassPath cp = new ClassPath(cmd.getXjreOption(),cmd.getCpOption());
        System.out.println("classpath:"+cp.printAbsPath()+" class:"+cmd.getClazz()+" args:"+cmd.args);
        byte[] data=cp.readClass(cmd.getClazz());
        System.out.println(Arrays.toString(data));
    }
}

大功告成,執行

參考資料:https://zachaxy.github.io