fast-loader(二)设计

分析

这是一个全新的工具,由我们设计的全新工具,所以我们可以来规划它未来长什么样子。
我们在上一篇:fast-loader(一)起源里讨论过,它最最最基本需要达到的目标应该是对类的按需装载,从而节约资源提升性能和资源利用率。
为了达到这个目标,我们就不能让JVM自行装载业务程序包,而是JVM装载fast-loader,由fast-loader对业务程序包进行装载,如上一篇的图
fast-loader(二)设计
因此,对于JVM而言,它并不清楚业务程序包的存在,而fast-loader需要关心的就是业务程序包,相当于fast-loader接管了JVM对业务程序运行的能力。
届时,对业务程序的运行将变成如下

站在用户的角度思考问题,与客户深入沟通,找到衢江网站设计与衢江网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站设计制作、网站建设、企业官网、英文网站、手机端网站、网站推广、申请域名网站空间、企业邮箱。业务覆盖衢江地区。

java -jar fast-loader.jar run some-biz-program

所以,fast-loader应该具备启动一个应用程序main方法的能力,同时,它还是一个应用程序
fast-loader为了启动应用程序,如果目标是jar包的话,需要能将jar装入内存,但如果整体装入的话,将会导致内存占用,这样fast-loader就无法节约内存,因此需要避免jar直接装入内存的情况。
考虑到jar包本质上是zip格式,同时参考tomcat对war包的处理逻辑,fast-loader首次启动时可以将jar进行解压到文件夹,再按需进行读取文件,这样虽然首次启动慢了些,但之后运行效率将大大提升。
因此,fast-loader的需要能对jar进行解压缩到文件的能力。
一个程序不仅仅是一个jar包,它可能是一堆jar的组成,所以,fast-loader还需要能将这堆jar解压后管理起来的能力。
程序运行过程中会访问各类资源,特别是springboot打包结构里,jar包内又包含jar包,考虑到springboot的风行程度,fast-loader需要具备对springboot包格式的兼容能力。
整理到一起

  • 它是一个应用程序,可直接启动
  • 它能启动jar包内的main方法
  • 它能够对jar包解压到文件夹中
  • 对于很多的jar包,它能够将这些解压缩出来的文件管理起来
  • 鉴于springboot的流行程度,需要兼容它的包格式

设计

前面定义了fast-loader的能力,我们再进行一个简单梳理,看看它的调用应该是怎么进行的
fast-loader(二)设计
所以,从用例的角度来看的话,它应该是这样的
fast-loader(二)设计
再对它的能力进行对齐和归类,如下
fast-loader(二)设计
各自职责如下

  • FastLoaderMain:Main方法所在类,负责启动业务APP并调用其main方法;
  • FastClassLoader:负责转载业务类定义到内存中;
  • JarUtil:负责解压缩业务jar包,如果存在多个jar包的话,还需要解压到对应的文件夹中;
  • FileUtil:负责对解压缩出来的.class以及资源文件进行管理;

转换成类图
fast-loader(二)设计
当然,除了这些类之外,肯定还有其它类存在,但这几个类为核心。
jar解压缩至指定的文件夹,考虑到配置的简便以及第三方库的数量较多,可以设计为
fast-loader(二)设计

优化

一、包冲突的优化

对存在多个相同版本的包的情况下,考虑到jar文件名称不同(自带版本号),将会释放到不同文件夹当中。
但是,在查找类文件的时候,由于包冲突原因可能导致查找时候无法明确指定那个版本,因此这里设定规则:

但出现同类库多个版本时,直接使用最新版本

为了适配这个规则,我们需要对以上解压缩的文件夹进行调整,包括

  • jar包解压缩文件夹名称不以jar名称命名,而是以不带版本号的包名作为文件夹名称
  • 解压缩进文件夹时,默认在包所在文件夹加入子文件夹,其名称为版本号;当没有版本号时,以“default”命名

变更后的文件夹格式如下
fast-loader(二)设计

二、加载速度的优化

fast-loader首次加载时涉及解压,会导致启动速度比之前下降,而首次之后因为按需加载,总体性能可以比原先得到提升,但是每次都去读取磁盘也是一种比较慢的方式,可以考虑设计优化。
实际app执行中,多数情况下,无论多少次的启动,其使用的类都基本相同。
基于这个理念,我们可以考虑将app第一次启动到关闭过程中使用到的类复制到另外一个文件夹中,之后app启动时默认装载此路径下所有类,这样就可以类似缓存的作用,一定程度上加快启动速度。
所以,文件夹部分还可以做这样的优化
fast-loader(二)设计
其中used文件夹为所有类定义后需要写入的文件夹,也是所有使用过的类的缓存文件夹。
这里有个小点需要注意,写入缓存成功与否不影响类定义,所以应该采用异步写入以避免堵塞。

三、针对springboot的适配

springboot有它独有的包体结构,其定义在MATE-INF/MANIFEST.MF文件中,举个案例

Manifest-Version: 1.0
Implementation-Title: demo
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: me.van.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher

通过这个文件,我们需要去适配的包括

  • Spring-Boot-Classes:需要将所有类解压缩至应用包文件夹目录下;
  • Spring-Boot-Lib:依赖的第三方库,需要将其解压缩成jar包后再二次解压;

四、多个同名资源文件的优化

不同jar包可能存在同名的资源文件,而ClassLoader对资源文件的获取是允许多个同时获取到的,而我们调整了对资源位置的定义,那么我们也要去重写“如何读取资源文件”这类方法。
同名文件可能存在两类

  1. jar:同名一般是相同文件,处理方法
    1. 相同hash值,意味着文件内容相同,直接取其一即可;
    2. 不同hash值,需要判断其MANIFEST.MF文件中Manifest-Version的值,保留新版本的jar;
    3. 没有MANIFEST.MF文件或没有Manifest-Version键,取创建时间迟的jar;
  2. 其它:统一为资源文件,当出现相同时,除第一个文件外,其它命名后面加入.n(此处n为数字,有1开始顺序递增)。

设计完成了,实现部分我们留到下一篇继续分享。

fast-loader系列

  1. fast-loader(一)起源

文章名称:fast-loader(二)设计
URL分享:http://pwwzsj.com/article/jgcpdo.html