一、类路径

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]

最新文章

  1. ORA-04063: view "SYS.DBA_REGISTRY" has errors
  2. dedecms /include/helpers/archive.helper.php SQL Injection Vul
  3. Android DrawerLayout Plus 增强版抽屉菜单
  4. c 函数调用产生的汇编指令和数据在内存情况(1)
  5. php的swoole扩展中onclose和onconnect接口不被调用的问题
  6. apache缓存
  7. 8.WCF简化的 AJAX(*)
  8. Codeforces 474D Flowers dp(水
  9. 演练5-6:Contoso大学校园管理系统6
  10. JavaScript实战
  11. 《用Python做HTTP接口测试》学习感悟
  12. [Hdu1342] Lotto
  13. 【Caffe篇】--Caffe从入门到初始及各层介绍
  14. 最长共公子序列(LCS)
  15. python中sorted()和set()去重,排序
  16. JavaScript 之 预编译 作用域,作用域链
  17. 【杂谈】Remember-Me的实现
  18. CentOS6安装Jenkins
  19. 设计模式-装饰者模式(Decorator Pattern)
  20. LeetCode之Decode Ways

热门文章

  1. 个人总结OLinux上安装oracle11G Data Guard
  2. Spring Security 4 使用@PreAuthorize,@PostAuthorize, @Secured, EL实现方法安全
  3. 阿里云POLARDB 2.0重磅来袭!为何用户如此的期待?
  4. Android内核剖析读书笔记(1)—Framework概述
  5. HTTP Cookie header 中set-cookie格式
  6. CSS长度单位:px和pt的区别
  7. centos安装hdp
  8. 最优化方法系列:Adam+SGD-AMSGrad 重点
  9. day1_python流程控制、For循环
  10. zoj 3859 DoIt is Being Flooded (MFSet && Flood Fill)