JVM自定义类加载器在代码扩展性的实践

作者:微信小助手

发布时间:2022-03-17T08:20:06

作者:vivo互联网服务器团队-Wang Fei


一、背景


名单管理系统是手机上各个模块将需要管控的应用配置到文件中,然后下发到手机上进行应用管控的系统,比如各个应用的耗电量管控;各个模块的管控应用文件考虑到安全问题,有自己的不同的加密方式,按照以往的经验,我们可以利用模板方法+工厂模式来根据模块的类型来获取到不同的加密方法。代码类层次结构示意如下:



获取不同加密方法的类结构图


利用工厂模式和模板方法模式,在有新的加密方法时,我们可以通过添加新的handler来满足"对修改关闭,对扩展开放"的原则,但是这种方式不可避免的需要修改代码和需要重新发版本和上线。那么有没有更好的方式能够去解决这个问题,这里就是我们今天要重点讲的主题。


二、类加载的时机


 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称为连接(Linking)。这七个阶段的发生顺序如图1所示。



虽然classloader的加载过程有复杂的7步,但事实上除了加载之外的四步,其它都是由JVM虚拟机控制的,我们除了适应它的规范进行开发外,能够干预的空间并不多。而加载则是我们控制classloader实现特殊目的最重要的手段了。也是接下来我们介绍的重点了。


三、加载


“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段。在加载阶段,Java虚拟机需要完成以下三件事情:


  • 通过一个类的全限定名来获取定义此类的二进制字节流。

  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。


《Java虚拟机规范》对这三点没有进行特别具体的要求,从而留给虚拟机实现与Java应用的灵活度都是相当大的。例如“通过一个类的全限定名来获取定义此类的二进制字节流”这条规则,它并没有指明二 进制字节流必须得从某个Class文件中获取,确切地说是根本没有指明要从哪里获取、如何获取。比如我们可以从ZIP压缩包中读取、从网络中获取、运行时计算生成、由其他文件生成、从数据库中读取。也可以可以从加密文件中获取。