java 基于类路径搜索_用Go编写JVM之搜索class文件

这篇博客介绍了如何使用Go语言来模拟Java的类加载机制,特别是类路径搜索。文章详细讲解了启动类加载器、扩展类加载器和应用程序类加载器的工作原理,并通过实例展示了如何编写代码来实现这些功能。作者通过设置命令行参数,实现了对Java类的加载,并给出了详细的运行效果和代码实现。
摘要由CSDN通过智能技术生成

源于只因遇见Go,被Go的魅力所折服,慢慢将发生下面的故事

Java类加载机制

Java类加载器的作用,将class文件加载到内存。从Java开发人员的角度来说,类加载器可分为三种:

启动类加载器(Bootstrap ClassLoader):加载/jre/lib下的jar

扩展类加载器(Extension ClassLoader):加载/jre/lib/ext/下的jar

应用程序类加载器(Application ClassLoader):加载用户自己编写的class

大多数的加载器使用双亲委派模型,双亲委派模型的工作过程是: 如果一个类加载器收到了类加载器的请求,它首先不会自己去尝试加载这类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

1240

类加载流程图.jpg

实现思路

设置命令行参数,指定路径

实现类路径:启动类加载器,扩展类加载器,应用程序类加载器

将类路径抽象出来,这三个作为其子类

编码过程

编码过程从三方面介绍,首先是工程目录,让读者可以清晰看见。其次是运行效果,读者可根据实现的搜索class文件的效果如何,选择是否继续往下观看。源码请移步传送门

工程目录

运行效果

代码实现

工程目录

gopath:代码存放区(workspace)

gopath/src:存放源码的区域

jvmgo:项目名

ch02:子文件

ch02/classpath/:这次主要编写的代码

1240

project02.png

运行效果

在ch02/下编译: go build -o readClass 后所在当前目录下便生成可执行文件readClass

验证通过启动类或扩展类加载器加载java.lang.Object,-Xjre是预定义的命令行参数,"/home/sprint/java/utils/jdk1.8.0_91/jre"指定jre路径,效果图如下:

1240

02_result_01.png

验证通过应用程序类加载器加载ch02/Testclass(自己编写的类),如果不指定路径,默认当前路径。-cp是预定义的命令行参数,""代表当前目录,效果图如下:

1240

02_result_02.png

代码实现

在terminal.go中预定义Xjre命令行参数

//定义Termial结构体

type Terminal struct {

//...省略字段,

XjreOption string // 添加XjreOption字段

}

func parseTerminal() *Terminal {

terminal := &Terminal{}

//省略部分代码...

flag.StringVar(&terminal.XjreOption, "Xjre", "", "path to jre")

flag.Parse()

//省略部分代码...

return terminal

}

编写类路径及其子类

总类路径包含:启动类加载器路径,扩展类加载器路径,应用程序类加载器路径。在ch02/classpath/下编写classpath.go,定义类路径如下:

type Classpath struct {

//启动类加载器

bootClasspath Entry

//扩展类加载器

extClasspath Entry

//应用程序加载器

userClasspath Entry

}

每种类加载器都有相同的方法,这时定义一个接口开表示类路径项。在ch02/classpath/下编写entry.go文件定义Entry接口:

type Entry interface {

//负责寻找和加载class

readClass(className string) ([]byte, Entry, error) //Go函数或方法运行返回多个值

//类似toString()

String() string

}

DirEntry代表:应用程序类加载器,用于加载文件目录中的class。ZipEntry代表:应用启动类和扩展类加载器,用于加载jar/zip文件。CompositeEntry和WildcardEntry代表:Entry的集合。在ch02/classpath下创建entry_dir.go, entry_zip.go,entry_composite.go,entry_wildcard.go文件实现Entry接口。Go的实现接口的方式与Java有所不同,想体验的话,动手实现下,体验下Go的魅力吧!由于考虑篇幅,所以接口具体实现不贴代码了,具体源码请移步传送门

测试执行

编写完上述代码后,更改ch02/main.go中的startJVM()函数来进行测试。

func startJVM(terminal *Terminal) {

cp := classpath.Parse(terminal.XjreOption, terminal.cpOption)

fmt.Printf("classpath:%v class:%v args:%v\n",

cp, terminal.class, terminal.args)

className := strings.Replace(terminal.class, ".", "/", -1)

classData, _, err := cp.ReadClass(className)

if err != nil {

fmt.Printf("Could not find or load main class %s\n", terminal.class)

}

fmt.Printf("class data:%v\n", classData)

}

一切准备就绪后,在ch02文件下敲入go build -o readClass,便生成了readClass可执行文件。之后按照"运行效果图"中命令行进行执行测试吧!

小结与声明:一边进一步理解Java虚拟机,一边学习Go。知识主要来源于gitbook(下面有地址)和张秀宏老师的《自己动手编写Java虚拟机》,我的源代码与书上的源码稍微有些不同。

参考文献

有疑问加站长微信联系(非本文作者)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值