一、类路径

https://blog.csdn.net/THMAIL/article/details/70025366

二、准备工作

在%GOPATH%\src\jvmgo下创建classpath文件夹,修改cmd.go里的Cmd结构体,增加一句 XjreOption string 
,并在parseCmd()函数中增加一句flag.StringVar(&cmd.XjreOption,"Xjre","","path to jre"),在flag.Parse()语句的上面

三、实现

类路径由启动类路径、扩展类路径和用户类路径构成,此次套用组合模式来设计和实现类路径。在classpath中创建

1、Entry接口

文件名为entry.go

 package classpath

 import "os"
import "strings" // :(linux/unix) or ;(windows)
const pathListSeparator = string(os.PathListSeparator) type Entry interface {
// className: fully/qualified/ClassName.class
readClass(className string) ([]byte, Entry, error)
String() string
} func newEntry(path string) Entry {
if strings.Contains(path, pathListSeparator) {
return newCompositeEntry(path)
} if strings.HasSuffix(path, "*") {
return newWildcardEntry(path)
} if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") ||
strings.HasSuffix(path, ".zip") || strings.HasSuffix(path, ".ZIP") { return newZipEntry(path)
} return newDirEntry(path)
}

常量pathListSeparator存放路径分隔符,Entry接口有两个方法,readClass()负责寻找和加载class文件;string()用于返回变量的字符串表示。readClass()的用法,如果读取java.lang.Object类,应传入java\lang\Object.class,返回值就是读取的字节数据、最终定位到class文件的Entry,以及错误信息。

Entry接口有四个实现,分别是DirEntry、ZipEntry、CompositeEntry、WildcardEntry。

2、DirEntry

文件名为entry_dir.go

 package classpath

 import "io/ioutil"
import "path/filepath" type DirEntry struct {
absDir string
} func newDirEntry(path string) *DirEntry {
absDir, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return &DirEntry{absDir}
} func (self *DirEntry) readClass(className string) ([]byte, Entry, error) {
fileName := filepath.Join(self.absDir, className)
data, err := ioutil.ReadFile(fileName)
return data, self, err
} func (self *DirEntry) String() string {
return self.absDir
}

DirEntry有一个用于存放目录绝对路径的字段

newDirEntry()先把参数转换成绝对路径,若转换出错,则panic终止程序,否则创建DirEntry实例并返回。

readClass()先把目录和class文件拼成一条完整路径,调用ioutil包的ReadFile()函数读取class文件内容,最后返回。String()函数直接返回目录。

3、ZipEntry

在文件entry_zip.go中,表示ZIP或JAR文件形式的类路径

 package classpath

 import "archive/zip"
import "errors"
import "io/ioutil"
import "path/filepath" type ZipEntry struct {
absPath string
} func newZipEntry(path string) *ZipEntry {
absPath, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return &ZipEntry{absPath}
} func (self *ZipEntry) readClass(className string) ([]byte, Entry, error) {
r, err := zip.OpenReader(self.absPath)
if err != nil {
return nil, nil, err
} defer r.Close()
for _, f := range r.File {
if f.Name == className {
rc, err := f.Open()
if err != nil {
return nil, nil, err
} defer rc.Close()
data, err := ioutil.ReadAll(rc)
if err != nil {
return nil, nil, err
} return data, self, nil
}
} return nil, nil, errors.New("class not found: " + className)
} func (self *ZipEntry) String() string {
return self.absPath
}

absPath存放ZIP或JAR文件的句对路径,newZipEntry()和String()和DirEntry大致相同,

readClass()函数从ZIP文件中读取class文件,首先打开ZIP文件,若出错,直接返回。然后遍历ZIP压缩包里的文件,看是否能找到class文件,若找到,则打开class文件,读取里面内容,并返回,若找不到或出现其他错误,返回错误信息,使用defer确保打开的文件能关闭。

readClass()方法每次都要打开和关闭ZIP文件,效率并不是很高。可以参考下面进行优化

 package classpath

 import "archive/zip"
import "errors"
import "io/ioutil"
import "path/filepath" type ZipEntry2 struct {
absPath string
zipRC *zip.ReadCloser
} func newZipEntry2(path string) *ZipEntry2 {
absPath, err := filepath.Abs(path)
if err != nil {
panic(err)
} return &ZipEntry2{absPath, nil}
} func (self *ZipEntry2) readClass(className string) ([]byte, Entry, error) {
if self.zipRC == nil {
err := self.openJar()
if err != nil {
return nil, nil, err
}
} classFile := self.findClass(className)
if classFile == nil {
return nil, nil, errors.New("class not found: " + className)
} data, err := readClass(classFile)
return data, self, err
} // todo: close zip
func (self *ZipEntry2) openJar() error {
r, err := zip.OpenReader(self.absPath)
if err == nil {
self.zipRC = r
}
return err
} func (self *ZipEntry2) findClass(className string) *zip.File {
for _, f := range self.zipRC.File {
if f.Name == className {
return f
}
}
return nil
} func readClass(classFile *zip.File) ([]byte, error) {
rc, err := classFile.Open()
if err != nil {
return nil, err
}
// read class data
data, err := ioutil.ReadAll(rc)
rc.Close()
if err != nil {
return nil, err
}
return data, nil
} func (self *ZipEntry2) String() string {
return self.absPath
}

readClass()函数通过调用openJar()打开ZIP文件,通过findClass()查找class文件,再通过readClass()参数是*zip.File的函数读取class文件内容。

4、CompositeEntry

在entry_composite.go文件中创建

package classpath

import "errors"
import "strings" type CompositeEntry []Entry func newCompositeEntry(pathList string) CompositeEntry {
compositeEntry := []Entry{} for _, path := range strings.Split(pathList, pathListSeparator) {
entry := newEntry(path)
compositeEntry = append(compositeEntry, entry)
} return compositeEntry
} func (self CompositeEntry) readClass(className string) ([]byte, Entry, error) {
for _, entry := range self {
data, from, err := entry.readClass(className)
if err == nil {
return data, from, nil
}
} return nil, nil, errors.New("class not found: " + className)
} func (self CompositeEntry) String() string {
strs := make([]string, len(self)) for i, entry := range self {
strs[i] = entry.String()
} return strings.Join(strs, pathListSeparator)
}

newCompositeEntry()把参数按分隔符分成小路径然后把每个小路径转换成具体的Entry实例。

readClass()是依次调用每一个字路径的readClass()函数,若遇到错误,则继续,若找到class文件则返回,否则返回错误。

string()函数调用每一个字路径的String()函数,把得到的字符串用路径分隔符拼接起来。

5、WildcardEntry

创建entry_wildcard.go文件

 package classpath

 import "os"
import "path/filepath"
import "strings" func newWildcardEntry(path string) CompositeEntry {
baseDir := path[:len(path)-1] // remove *
compositeEntry := []Entry{} walkFn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() && path != baseDir {
return filepath.SkipDir
}
if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") {
jarEntry := newZipEntry(path)
compositeEntry = append(compositeEntry, jarEntry)
}
return nil
} filepath.Walk(baseDir, walkFn) return compositeEntry
}

newWildcardEntry()函数使用CompositeEntry结构体,首先把末尾的*号去掉,得到baseDir,调用filepath包的Walk()函数遍历baseDir创建ZipEntry

6、classpath

在classpath.go中创建

 package classpath

 import "os"
import "path/filepath" type Classpath struct {
bootClasspath Entry
extClasspath Entry
userClasspath Entry
} func Parse(jreOption, cpOption string) *Classpath {
cp := &Classpath{}
cp.parseBootAndExtClasspath(jreOption)
cp.parseUserClasspath(cpOption)
return cp
} func (self *Classpath) parseBootAndExtClasspath(jreOption string) {
jreDir := getJreDir(jreOption) // jre/lib/*
jreLibPath := filepath.Join(jreDir, "lib", "*")
self.bootClasspath = newWildcardEntry(jreLibPath) // jre/lib/ext/*
jreExtPath := filepath.Join(jreDir, "lib", "ext", "*")
self.extClasspath = newWildcardEntry(jreExtPath)
} func getJreDir(jreOption string) string {
if jreOption != "" && exists(jreOption) {
return jreOption
}
if exists("./jre") {
return "./jre"
}
if jh := os.Getenv("JAVA_HOME"); jh != "" {
return filepath.Join(jh, "jre")
}
panic("Can not find jre folder!")
} func exists(path string) bool {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
} func (self *Classpath) parseUserClasspath(cpOption string) {
if cpOption == "" {
cpOption = "."
}
self.userClasspath = newEntry(cpOption)
} // className: fully/qualified/ClassName
func (self *Classpath) ReadClass(className string) ([]byte, Entry, error) {
className = className + ".class"
if data, entry, err := self.bootClasspath.readClass(className); err == nil {
return data, entry, err
}
if data, entry, err := self.extClasspath.readClass(className); err == nil {
return data, entry, err
}
return self.userClasspath.readClass(className)
} func (self *Classpath) String() string {
return self.userClasspath.String()
}

Calsspath结构体有三种字段对应三种类路径。Parse()函数使用-Xjre()选项解析启动类路径和扩展类路径,使用-classpath选项解析用户类路径。parseBootAndExtClasspath()函数。

getJreDir优先使用用户输入的-Xjre作为jre路径,若没有则在当前目录下查找jre目录,若没有,尝试JAVA_HOME环境变量。

exists()判断目录是否存在。

parsseUserClasspath()函数,若没有提供-cp选项,则使用当前目录为用户类路径,readClass()依次从启动类路径、扩展类路径和用户类路径中搜素class文件。

注意:传递给readClass()方法的类名不包含".class"后缀。

String()返回用户类路径的字符串。

四、测试

在main.go文件中添加两条import语句

import "strings"
import "jvmgo/classpath"

main()函数不变,重写startJVM()函数

 func startJVM(cmd *Cmd) {
cp:=classpath.Parse(cmd.XjreOption,cmd.cpOption)
fmt.Printf("classpath:%v class: %v args: %v\n",cp,cmd.class,cmd.args) className:= strings.Replace(cmd.class,".","/",-1) classData,_,err:=cp.ReadClass(className)
if(err !=nil){
fmt.Printf("Could not find or load main class %s \n",cmd.class)
return
} fmt.Printf("class Date:%v\n",classData)
}

然后运行go install jvmgo

在cmd中运行jvmgo.exe进行测试

PS D:\Program_Files\go\bin> .\jvmgo.exe  -Xjre "D:\APP\java\java1.8\jdk1.8.0_171\jre" java.lang.Object
classpath:D:\Program_Files\go\bin class: java.lang.Object args: []
class Date:[202 254 186 190 0 0 0 52 0 78 3 0 15 66 63 8 0 16 8 0 38 8 0 42 1 0 3 40 41 73 1 0 20 40 41 76 106 97 118 97 47 108 97 110 103 47 79 98 106 101 99 116 59 1 0 20 40 41 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 1 0 3 40 41 86 1 0 21 40 73 41 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 1 0 4 40 74 41 86 1 0 5 40 74 73 41 86 1 0 21 40 76 106 97 118 97 47 108 97 110 103 47 79 98 106 101 99 116 59 41 90 1 0 21 40 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 41 86 1 0 8 60 99 108 105 110 105 116 62 1 0 6 60 105 110 105 116 62 1 0 1 64 1 0 4 67 111 100 101 1 0 10 69 120 99 101 112 116 105 111 110 115 1 0 15 76 105 110 101 78 117 109 98 101 114 84 97 98 108 101 1 0 9 83 105 103 110 97 116 117 114 101 1 0 10 83 111 117 114 99 101 70 105 108 101 1 0 13 83 116 97 99 107 77 97 112 84 97 98 108 101 1 0 6 97 112 112 101 110 100 1 0 5 99 108 111 110 101 1 0 6 101 113 117 97 108 115 1 0 8 102 105 110 97 108 105 122 101 1 0 8 103 101 116 67 108 97 115 115 1 0 7 103 101 116 78 97 109 101 1 0 8 104 97 115 104 67 111 100 101 1 0 15 106 97 118 97 47 108 97 110 103 47 67 108 97 115 115 1 0 36 106 97 118 97 47 108 97 110 103 47 67 108 111 110 101 78 111 116 83 117 112 112 111 114 116 101 100 69 120 99 101 112 116 105 111 110 1 0 34 106 97 118 97 47 108 97 110 103 47 73 108 108 101 103 97 108 65 114 103 117 109 101 110 116 69 120 99 101 112 116 105 111 110 1 0 17 106 97 118 97 47 108 97 110 103 47 73 110 116 101 103 101 114 1 0 30 106 97 118 97 47 108 97 110 103 47 73 110 116 101 114 114 117 112 116 101 100 69 120 99 101 112 116 105 111 110 1 0 16 106 97 118 97 47 108 97 110 103 47 79 98 106 101 99 116 1 0 23 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 66 117 105 108 100 101 114 1 0 19 106 97 118 97 47 108 97 110 103 47 84 104 114 111 119 97 98 108 101 1 0 37 110 97 110 111 115 101 99 111 110 100 32 116 105 109 101 111 117 116 32 118 97 108 117 101 32 111 117 116 32 111 102 32 114 97 110 103 101 1 0 6 110 111 116 105 102 121 1 0 9 110 111 116 105 102 121 65 108 108 1 0 15 114 101 103 105 115 116 101 114 78 97 116 105 118 101 115 1 0 25 116 105 109 101 111 117 116 32 118 97 108 117 101 32 105 115 32 110 101 103 97 116 105 118 101 1 0 11 116 111 72 101 120 83 116 114 105 110 103 1 0 8 116 111 83 116 114 105 110 103 1 0 4 119 97 105 116 7 0 30 7 0 31 7 0 32 7 0 33 7 0 34 7 0 35 7 0 36 7 0 37 1 0 19 40 41 76 106 97 118 97 47 108 97 110 103 47 67 108 97 115 115 59 1 0 22 40 41 76 106 97 118 97 47 108 97 110 103 47 67 108 97 115 115 60 42 62 59 1 0 45 40 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 41 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 66 117 105 108 100 101 114 59 12 0 29 0 5 12 0 15 0 8 12 0 41 0 8 12 0 45 0 10 12 0 27 0 54 12 0 28 0 7 12 0 44 0 7 12 0 43 0 9 12 0 15 0 13 12 0 23 0 56 10 0 46 0 62 10 0 48 0 65 10 0 49 0 64 10 0 51 0 57 10 0 51 0 59 10 0 51 0 60 10 0 51 0 61 10 0 52 0 58 10 0 52 0 63 10 0 52 0 66 1 0 11 79 98 106 101 99 116 46 106 97 118 97 0 33 0 51 0 0 0 0 0 0 0 14 0 1 0 15 0 8 0 1 0 17 0 0 0 25 0 0 0 1 0 0 0 1 177 0 0 0 1 0 19 0 0 0 6 0 1 0 0 0 37 1 10 0 41 0 8 0 0 1 17 0 27 0 54 0 1 0 20 0 0 0 2 0 55 1 1 0 29 0 5 0 0 0 1 0 25 0 12 0 1 0 17 0 0 0 46 0 2 0 2 0 0 0 11 42 43 166 0 7 4 167 0 4 3 172 0 0 0 2 0 22 0 0 0 5 0 2 9 64 1 0 19 0 0 0 6 0 1 0 0 0 149 1 4 0 24 0 6 0 1 0 18 0 0 0 4 0 1 0 47 0 1 0 44 0 7 0 1 0 17 0 0 0 60 0 2 0 1 0 0 0 36 187 0 52 89 183 0 74 42 182 0 73 182 0 67 182 0 76 18 2 182 0 76 42 182 0 70 184 0 69 182 0 76 182 0 75 176 0 0 0 1 0 19 0 0 0 6 0 1 0 0 0 236 1 17 0 39 0 8 0 0 1 17 0 40 0 8 0 0 1 17 0 45 0 10 0 1 0 18 0 0 0 4 0 1 0 50 0 17 0 45 0 11 0 2 0 17 0 0 0 114 0 4 0 4 0 0 0 50 31 9 148 156 0 13 187 0 48 89 18 4 183 0 68 191 29 155 0 9 29 18 1 164 0 13 187 0 48 89 18 3 183 0 68 191 29 158 0 7 31 10 97 64 42 31 182 0 72 177 0 0 0 2 0 22 0 0 0 6 0 4 16 9 9 7 0 19 0 0 0 34 0 8 0 0 1 191 0 6 1 192 0 16 1 195 0 26 1 196 0 36 1 200 0 40 1 201 0 44 1 204 0 49 1 205 0 18 0 0 0 4 0 1 0 50 0 17 0 45 0 8 0 2 0 17 0 0 0 34 0 3 0 1 0 0 0 6 42 9 182 0 72 177 0 0 0 1 0 19 0 0 0 10 0 2 0 0 1 246 0 5 1 247 0 18 0 0 0 4 0 1 0 50 0 4 0 26 0 8 0 2 0 17 0 0 0 25 0 0 0 1 0 0 0 1 177 0 0 0 1 0 19 0 0 0 6 0 1 0 0 2 43 0 18 0 0 0 4 0 1 0 53 0 8 0 14 0 8 0 1 0 17 0 0 0 32 0 0 0 0 0 0 0 4 184 0 71 177 0 0 0 1 0 19 0 0 0 10 0 2 0 0 0 41 0 3 0 42 0 1 0 21 0 0 0 2 0 77]

二、搜索class文件的更多相关文章

  1. linux下gcc默认搜索头文件及库文件的路径

    一.头文件gcc 在编译时如何去寻找所需要的头文件:※所以header file的搜寻会从-I开始※然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC ...

  2. java实现基于关键字的文件夹(文件)的搜索、文件夹(文件)的复制、删除

    最近在做一个项目,需要实现这几项功能,上网查了很多资料,自己研究了好几天终于实现了,现在与大家分享一下. 一.JAVA实现文件夹的搜索   在百度搜索N个技术文章,从哪些大牛们共享的资料中终于写出了我 ...

  3. 获取文件的缩略图Thumbnail和通过 AQS - Advanced Query Syntax 搜索本地文件

    演示如何获取文件的缩略图 FileSystem/ThumbnailAccess.xaml <Page x:Class="XamlDemo.FileSystem.ThumbnailAcc ...

  4. Linux如何搜索查找文件里面内容

    在Linux系统当中,如何搜.索查找文件里面的内容呢? 这个应该是系统维护.管理当中遇到最常见的需求.那么下面介绍,总结一下如何搜索.查找文件当中的内容. 搜索.查找文件当中的内容,一般最常用的是gr ...

  5. C#搜索指定文件夹内的符合要求的文件

    下面的列子是文件的模糊查找, 具体功能是:选定文件夹,搜索所有文件命中包含“_bui”字样的shp图层(后缀为.shp)并将信息显示在ListView中.实际应用中可随便修改. 这里采用递归方法进行深 ...

  6. Java递归搜索指定文件夹下的匹配文件

    import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Queue; /** ...

  7. PostgreSQL的存储系统二:REDOLOG文件存储结构二

    REDOLOG文件里的用户数据和数据文件里的用户数据存储结构相同 几个月前同事给台湾一家公司培训<pg9 ad admin>时,有个学员提及WAL里记录的内容为Query时的SQL语句(比 ...

  8. 重新想象 Windows 8 Store Apps (22) - 文件系统: 访问文件夹和文件, 通过 AQS 搜索本地文件

    原文:重新想象 Windows 8 Store Apps (22) - 文件系统: 访问文件夹和文件, 通过 AQS 搜索本地文件 [源码下载] 重新想象 Windows 8 Store Apps ( ...

  9. 从零开始学C++之IO流类库(二):文件流(fstream, ifstream, ofstream)的打开关闭、流状态

    一.文件流 ofstream,由ostream派生而来,用于写文件 ifstream,由istream派生而来, 用于读文件 fstream,由iostream派生而来,用于读写文件 二.打开文件 说 ...

  10. 【算法系列学习三】[kuangbin带你飞]专题二 搜索进阶 之 A-Eight 反向bfs打表和康拓展开

    [kuangbin带你飞]专题二 搜索进阶 之 A-Eight 这是一道经典的八数码问题.首先,简单介绍一下八数码问题: 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的 ...

随机推荐

  1. SpringMVC的简单传值总结

    之前学习SpringMVC时感觉他的传值很神奇:简便,快捷,高效. 今天写几个简单的传值与大家分享,希望能对大家有帮助. 一. 从后往前传: (1) @Controller @RequestMappi ...

  2. 2019-7-4-win10-uwp-处理用户点击关闭按钮

    title author date CreateTime categories win10 uwp 处理用户点击关闭按钮 lindexi 2019-07-04 09:28:57 +0800 2019- ...

  3. 2019-11-1-asp-dotnet-core-简单开发P2P中央服务器

    title author date CreateTime categories asp dotnet core 简单开发P2P中央服务器 lindexi 2019-11-01 19:40:33 +08 ...

  4. docker查看运行容器详细信息

    使用docker ps命令可以查看所有正在运行中的容器列表, 使用docker inspect命令我们可以查看更详细的关于某一个容器的信息. $ docker inspect 容器id/image

  5. 【NS2】ns2 otcl与c++关联(转载)

    最近几天,对ns2进行研究,ns2为什么要使用两种语言,因为C++执行速度快,因此对于一些不需要经常改变的东西:例如包的发送.而对于需要经常进行修改的就不能够使用C++,而使用OTcl脚本语言.所有O ...

  6. 卸载ROS命令

    ROS有问题需要卸载只需输入以下命令: sudo apt-get purge ros-* sudo rm -rf /etc/ros

  7. windows 下的 Apache 二级域名 目录绑定配置

    通常我们注册的域名都是顶级域名  如 www.potatog.com,我们希望这个域名可以访问服务器的不同网站或者不同功能等等 可能会这样 www.potatog.com/api 或者 www.pot ...

  8. hdu 1077 (圆交)

    Problem - 1077 我们可以知道,当这个单位圆可以覆盖到最多的点的时候,必定最少有两个点位于这个圆的圆周上,于是就有网上众多的O(N^3)的枚举两个在圆上的点的暴搜做法. 然而这题是可以用圆 ...

  9. @游记@ CQOI2019(十二省联考)

    目录 @day - 0@ @day - 1@ @day - 2@ @后记@ 我只是来打酱油哒-- 顶多能进个 E 类继续打酱油. 原本还在互奶 A 队,结果现在--铁定进不了队啦. 对初中生的歧视啊 ...

  10. laravel博客后台操作步骤