二、搜索class文件

一、类路径

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

二、准备工作

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

三、实现

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

1、Entry接口

文件名为entry.go

 1 package classpath
 2 
 3 import "os"
 4 import "strings"
 5 
 6 // :(linux/unix) or ;(windows)
 7 const pathListSeparator = string(os.PathListSeparator)
 8 
 9 type Entry interface {
10     // className: fully/qualified/ClassName.class
11     readClass(className string) ([]byte, Entry, error)
12     String() string
13 }
14 
15 func newEntry(path string) Entry {
16     if strings.Contains(path, pathListSeparator) {
17         return newCompositeEntry(path)
18     }
19 
20     if strings.HasSuffix(path, "*") {
21         return newWildcardEntry(path)
22     }
23 
24     if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") ||
25         strings.HasSuffix(path, ".zip") || strings.HasSuffix(path, ".ZIP") {
26 
27         return newZipEntry(path)
28     }
29 
30     return newDirEntry(path)
31 }

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

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

2、DirEntry

文件名为entry_dir.go

 1 package classpath
 2 
 3 import "io/ioutil"
 4 import "path/filepath"
 5 
 6 type DirEntry struct {
 7     absDir string
 8 }
 9 
10 func newDirEntry(path string) *DirEntry {
11     absDir, err := filepath.Abs(path)
12     if err != nil {
13         panic(err)
14     }
15     return &DirEntry{absDir}
16 }
17 
18 func (self *DirEntry) readClass(className string) ([]byte, Entry, error) {
19     fileName := filepath.Join(self.absDir, className)
20     data, err := ioutil.ReadFile(fileName)
21     return data, self, err
22 }
23 
24 func (self *DirEntry) String() string {
25     return self.absDir
26 }

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

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

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

3、ZipEntry

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

 1 package classpath
 2 
 3 import "archive/zip"
 4 import "errors"
 5 import "io/ioutil"
 6 import "path/filepath"
 7 
 8 type ZipEntry struct {
 9     absPath string
10 }
11 
12 func newZipEntry(path string) *ZipEntry {
13     absPath, err := filepath.Abs(path)
14     if err != nil {
15         panic(err)
16     }
17     return &ZipEntry{absPath}
18 }
19 
20 func (self *ZipEntry) readClass(className string) ([]byte, Entry, error) {
21     r, err := zip.OpenReader(self.absPath)
22     if err != nil {
23         return nil, nil, err
24     }
25 
26     defer r.Close()
27     for _, f := range r.File {
28         if f.Name == className {
29             rc, err := f.Open()
30             if err != nil {
31                 return nil, nil, err
32             }
33 
34             defer rc.Close()
35             data, err := ioutil.ReadAll(rc)
36             if err != nil {
37                 return nil, nil, err
38             }
39 
40             return data, self, nil
41         }
42     }
43 
44     return nil, nil, errors.New("class not found: " + className)
45 }
46 
47 func (self *ZipEntry) String() string {
48     return self.absPath
49 }

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

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

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

 1 package classpath
 2 
 3 import "archive/zip"
 4 import "errors"
 5 import "io/ioutil"
 6 import "path/filepath"
 7 
 8 type ZipEntry2 struct {
 9     absPath string
10     zipRC   *zip.ReadCloser
11 }
12 
13 func newZipEntry2(path string) *ZipEntry2 {
14     absPath, err := filepath.Abs(path)
15     if err != nil {
16         panic(err)
17     }
18 
19     return &ZipEntry2{absPath, nil}
20 }
21 
22 func (self *ZipEntry2) readClass(className string) ([]byte, Entry, error) {
23     if self.zipRC == nil {
24         err := self.openJar()
25         if err != nil {
26             return nil, nil, err
27         }
28     }
29 
30     classFile := self.findClass(className)
31     if classFile == nil {
32         return nil, nil, errors.New("class not found: " + className)
33     }
34 
35     data, err := readClass(classFile)
36     return data, self, err
37 }
38 
39 // todo: close zip
40 func (self *ZipEntry2) openJar() error {
41     r, err := zip.OpenReader(self.absPath)
42     if err == nil {
43         self.zipRC = r
44     }
45     return err
46 }
47 
48 func (self *ZipEntry2) findClass(className string) *zip.File {
49     for _, f := range self.zipRC.File {
50         if f.Name == className {
51             return f
52         }
53     }
54     return nil
55 }
56 
57 func readClass(classFile *zip.File) ([]byte, error) {
58     rc, err := classFile.Open()
59     if err != nil {
60         return nil, err
61     }
62     // read class data
63     data, err := ioutil.ReadAll(rc)
64     rc.Close()
65     if err != nil {
66         return nil, err
67     }
68     return data, nil
69 }
70 
71 func (self *ZipEntry2) String() string {
72     return self.absPath
73 }

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文件

 1 package classpath
 2 
 3 import "os"
 4 import "path/filepath"
 5 import "strings"
 6 
 7 func newWildcardEntry(path string) CompositeEntry {
 8     baseDir := path[:len(path)-1] // remove *
 9     compositeEntry := []Entry{}
10 
11     walkFn := func(path string, info os.FileInfo, err error) error {
12         if err != nil {
13             return err
14         }
15         if info.IsDir() && path != baseDir {
16             return filepath.SkipDir
17         }
18         if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") {
19             jarEntry := newZipEntry(path)
20             compositeEntry = append(compositeEntry, jarEntry)
21         }
22         return nil
23     }
24 
25     filepath.Walk(baseDir, walkFn)
26 
27     return compositeEntry
28 }

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

6、classpath

在classpath.go中创建

 1 package classpath
 2 
 3 import "os"
 4 import "path/filepath"
 5 
 6 type Classpath struct {
 7     bootClasspath Entry
 8     extClasspath  Entry
 9     userClasspath Entry
10 }
11 
12 func Parse(jreOption, cpOption string) *Classpath {
13     cp := &Classpath{}
14     cp.parseBootAndExtClasspath(jreOption)
15     cp.parseUserClasspath(cpOption)
16     return cp
17 }
18 
19 func (self *Classpath) parseBootAndExtClasspath(jreOption string) {
20     jreDir := getJreDir(jreOption)
21 
22     // jre/lib/*
23     jreLibPath := filepath.Join(jreDir, "lib", "*")
24     self.bootClasspath = newWildcardEntry(jreLibPath)
25 
26     // jre/lib/ext/*
27     jreExtPath := filepath.Join(jreDir, "lib", "ext", "*")
28     self.extClasspath = newWildcardEntry(jreExtPath)
29 }
30 
31 func getJreDir(jreOption string) string {
32     if jreOption != "" && exists(jreOption) {
33         return jreOption
34     }
35     if exists("./jre") {
36         return "./jre"
37     }
38     if jh := os.Getenv("JAVA_HOME"); jh != "" {
39         return filepath.Join(jh, "jre")
40     }
41     panic("Can not find jre folder!")
42 }
43 
44 func exists(path string) bool {
45     if _, err := os.Stat(path); err != nil {
46         if os.IsNotExist(err) {
47             return false
48         }
49     }
50     return true
51 }
52 
53 func (self *Classpath) parseUserClasspath(cpOption string) {
54     if cpOption == "" {
55         cpOption = "."
56     }
57     self.userClasspath = newEntry(cpOption)
58 }
59 
60 // className: fully/qualified/ClassName
61 func (self *Classpath) ReadClass(className string) ([]byte, Entry, error) {
62     className = className + ".class"
63     if data, entry, err := self.bootClasspath.readClass(className); err == nil {
64         return data, entry, err
65     }
66     if data, entry, err := self.extClasspath.readClass(className); err == nil {
67         return data, entry, err
68     }
69     return self.userClasspath.readClass(className)
70 }
71 
72 func (self *Classpath) String() string {
73     return self.userClasspath.String()
74 }

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()函数

 1 func startJVM(cmd *Cmd) {
 2     cp:=classpath.Parse(cmd.XjreOption,cmd.cpOption)
 3     fmt.Printf("classpath:%v class: %v args: %v
",cp,cmd.class,cmd.args)
 4     
 5     className:= strings.Replace(cmd.class,".","/",-1)
 6     
 7     classData,_,err:=cp.ReadClass(className)
 8     if(err !=nil){
 9         fmt.Printf("Could not find or load main class %s 
",cmd.class)
10         return
11     }
12     
13     fmt.Printf("class Date:%v
",classData)
14 }

然后运行go install jvmgo

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

PS D:Program_Filesgoin> .jvmgo.exe  -Xjre "D:APPjavajava1.8jdk1.8.0_171jre" java.lang.Object
classpath:D:Program_Filesgoin 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]
原文地址:https://www.cnblogs.com/pingxin/p/p00081.html