Refresh your Java skills–聊聊Java9 中模块化所基于的文件系统 JRTFS
说到文件系统我们很容易就想到Linux,windows操作系统的文件系统,对应到我们的生活中,我们想去一所学校找到某个学生,假如你不了解学号所代表的意义,那就只能是一点一点的找了,不过绝对知道这个学生是几年级,然后一个班一个班的找,假如了解学号的意义的话我们就可以直接定位到哪一栋楼,哪一间教室。
说的再直白点,不就是是个找啊找啊找朋友的游戏么。这也就是我们排序查找的算法了,而面向大量有用数据最好的实践就是用树形结构来统筹,于是我们的数据库的索引,我们的zookeeper的节点管理,小到我们Java里使用的红黑树,以及对hashmap的优化等等,就是因为其复杂度可以降到最低,只需要凭借树的高度就可以快速找到我们所要找的数据了。
说了这么多,就是想要表达的是,我们的Java9中所设计的全新的JRTFS也是基于树来表达的。
文件系统的设计
我们往往会将一堆数据分析其成分,然后抽取出结构来对其组织,往往我们碰到的最多的是表结构和其数据,结构定义和数据要分开存放,这里我们首先对其进行结构的定义,接着我们要将每一份数据进行穿针引线,做成一个体系,其实就是一个索引体系,我们要做的就是对其每一个节点的管理。而最后所建立起的索引系统可以作为一个专门的文件来存放(windows系统下面的话请参照C:\Program Files\Java\jdk-9.0.1\lib\modules这个文件),我们的结构定义作为一个专门的jar文件来存放(windows系统下面的话请参照C:\Program Files\Java\jdk-9.0.1\lib\jrt-fs.jar)
组织结构定义中基本文件的设计
我们可以参考Linux文件系统,其一个文件应该包含什么样的基本属性:name,可读性,创建时间,最后修改时间,最后访问时间。
我们把我们的目光转向jdk.internal.jrtfs这个包下。找到jdk.internal.jrtfs.JrtFileAttributes,因为Java9要兼容Java8的东西,所以势必要做两种不一样的考虑,那么此处就应该开始做一个岔路口。里面定义了上面所说的这些基本属性。同样,我们可以看到它是基于树的节点控制来做到的。
1  | /**  | 
这样,我们就可以有组成一个树形文件系统的节点定义了。
文件系统镜像的入口设定
接着通过jdk.internal.jrtfs.SystemImage来作为文件系统的加载入口,在初始化这个类的时候,会首先把静态代码块给执行,接着,我们会在jdk.internal.jrtfs.JrtFileSystem 其构造函数中发现其调用了SystemImage.open()方法,可以知道其首先会检查C:\Program Files\Java\jdk-9.0.1\lib\modules这个文件是否存在,存在,就使用jdk.internal.jimage.ImageReader中的静态内部类jdk.internal.jimage.ImageReader.SharedImageReader来对此文件的进行读取然后建立相应的文件系统镜像:
1  | abstract class SystemImage {  | 
也就是说,上面这个类的定义,我们可以把启动封装一个open方法,最后在大一统实现文件系统的时候集中调用,每个类做好自己那份事情就好。
jdk.internal.jrtfs.JrtFileSystem的构造器:
1  | class JrtFileSystem extends FileSystem {  | 
提供结构定义并设定加载文件系统入口
通过前面提到的索引数据和结构定义数据分开的可以知道,我们的结构定义也是需要有的,那么,走进jdk.internal.jrtfs.JrtFileSystemProvider来看看其内在乾坤,从下面的源码中可以知道,JrtFileSystemProvider 会判断区分当前的环境状态(这里要求必须存在C:\Program Files\Java\jdk-9.0.1\lib\jrt-fs.jar),首先拿到jrt-fs.jar的路径,其实通过URLClassLoader.loadClass(String name, boolean resolve)得到Classloader实例,加载完这些结构定义之后,返回一个FileSystem实例(return new JrtFileSystem(this, env);)
1  | public final class JrtFileSystemProvider extends FileSystemProvider {  | 
文件系统路径定义
既然是文件系统,路径这块总要有定义的,就好像Linux使用/作为根,对于Jrtfs来说,同样要有相应定义的。jdk.internal.jrtfs.JrtPath 就是jrt file systems关于Path的基本实现类。
作为一个Path其解析的肯定是一个URI字符串路径,对于操作字符串,我们用的比较多的有切分,而且字符串内部用的比较多的同样有offset,和判断/home/abc/ddd一样,我们通过确认/这个约定来对文件系统进行分层,确定父子 关系,就好像我们的/Base/A模块/B模块/C模块,要获取某些操作,我们都需要先对这个路径以/做偏移量操作,以方便快速获取到某模块的名字。而我们的很多方法刚开始都会调用initOffsets();,那我们来看看这个方法的具体操作:
1  | // create offset list if not already created  | 
然后再加入一个JrtFileSystem,自然很多事情就可以做到了,此处就不再多说了。
Jrt文件系统的文件存储实现
其实Jrt  file systems的文件存储实现很简单,可以说没什么内容,因为是内存里建立起来的镜像文件系统,它也只提供了一些基本的约束,如,文件系统应该以什么为开头等等。
1  | final class JrtFileStore extends FileStore {  | 
Jrtfs中文件属性视图的设定
我们在写web项目的时候,往往会使用DTO来展示这些公开的数据,对于文件系统中的文件也是,这就出现了文件属性视图的需求,包括读取和对这些公开属性的设定,比如文件的创建修改时间。
我们找到java.nio.file.attribute.BasicFileAttributeView这个接口,里面定义了上面所说的这些基本属性。然后我们通过jdk.internal.jrtfs.JrtFileAttributeView来对其进行实现。
我们可以通过文件系统类的类型是否相等来判断到底是使用老版本的通过classpath来加载的方式,还是通过Jrtfs的方式来加载。请看如下代码:
1  | ("unchecked") // Cast to V  | 
也可以通过一个String关键字来判断:
1  | static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {  | 
基本属性的话,首先对所操作属性进行判断了:
1  | static Object attribute(AttrID id, JrtFileAttributes jrtfas, boolean isJrtView) {  | 
这里的枚举类型,也是我们这个类中定义的:
1  | private static enum AttrID {  | 
就到此吧,关于更多对模块的解读,留在下一篇去说。