《自己動手寫java虛擬機器》學習筆記(四)-----搜尋class檔案(java)
阿新 • • 發佈:2018-11-23
專案地址: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