JVM搭建自定义加载器拓展性的实施

一、背景 【JVM搭建自定义加载器拓展性的实施】名单管理体系是手机上各个模块将需求管控的运用配备到文件中,然后下发到手机上进行运用管控的体系,比方各个运用的耗电量管控;各个模块的管控运用文件考虑到安全问题,有自己的不同的加密方法,按照以往的阅历,我们可以运用模板方法+工厂方式来依据模块的类型来获取到不同的加密方法。代码类层次结构暗示如下:
运用工厂方式和模板方法方式,在有新的加密方法时,我们可以通过添加新的handler来满足"对修正关闭,对扩展敞开"的准则,但是这种方法不可避免的需求修正代码和需求重新发版别和上线。那么有没有更好的方法可以去处理这个问题,这儿便是我们今日要要点讲的主题。
二、类加载的时机 一个类型从被加载到虚拟机内存中开端,到卸载出内存中止,它的整个生命周期将会阅历`
加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、运用(Using)和卸载(Unloading)

虽然classloader的加载进程有凌乱的7步,但事实上除了加载之外的四步,其它都是由JVM虚拟机操控的,我们除了习气它的标准进行开发外,可以干涉的空间并不多。而加载则是我们操控classloader完结特别目的最重要的方法了。也是接下来我们介绍的要点了。

protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader }if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }

loadClass加载方法流程:
判断此类是否已经加载;
如果父加载器不为null,则使用父加载器进行加载;反之,使用根加载器进行加载;
如果前面都没加载成功,则使用findClass方法进行加载。
所以,为了不影响类的加载过程,我们重写findClass方法即可简单方便的实现自定义类加载。
六、代码实现 6.1 实现自定义的类加载器
public class DynamicClassLoader extends ClassLoader { private static final String CLASS_EXTENSION = "class"; @Override public Class findClass(String encryptClassInfo) { EncryptClassInfo info = JSON.parseObject(encryptClassInfo, EncryptClassInfo.class); String filePath = info.getAbsoluteFilePath(); String systemPath = System.getProperty("java.io.tmpdir"); String normalizeFileName = FilenameUtils.normalize(filePath, true); if (StringUtils.isEmpty(normalizeFileName) || !normalizeFileName.startsWith(systemPath) ||getApkFileExtension(normalizeFileName) == null || !CLASS_EXTENSION.equals(getApkFileExtension(normalizeFileName))) { return null; } String className = info.getEncryptClassName(); byte[] classBytes = null; File customEncryptFile = new File(filePath); try { Path path = Paths.get(customEncryptFile.toURI()); classBytes = Files.readAllBytes(path); } catch (IOException e) { log.info("加密错误", e); }

## 三、加载 “加载”(Loading)阶段是整个“类加载”(Class Loading)进程中的一个阶段。在加载阶段,Java虚拟机需求完结以下三件作业:通过一个类的全限定名来获取定义此类的二进制字节省。 将这个字节省所代表的静态存储结构转化为方法区的运行时数据结构。 在内存中生成一个代表这个类的java.lang.Class目标,作为方法区这个类的各种数据的访问进口。 《Java虚拟机标准》对这三点没有进行特别具体的要求,然后留给虚拟机完结与Java运用的灵敏度都是相当大的。例如“通过一个类的全限定名来获取定义此类的二进制字节省”这条规矩,它并没有指明二 进制字节省必须得从某个Class文件中获取,切当地说是底子没有指明要从哪里获取、如何获取。比方我们可以从ZIP压缩包中读取、从网络中获取、运行时核算生成、由其他文件生成、从数据库中读取。也可以可以从加密文件中获取。从这儿我们可以看出,只需求我们可以获取到加密类的.class文件,我们就可以通过类加载器获取到对应的加密类class目标,从而通过反射去调用具体的加密方法。因而类加载器在.class文件的加载进程有着至关重要的地位。## 四、双亲委派模型 现在Java虚拟机现已存在三品种加载器,分别为发起类加载器、扩展类加载器和运用程序类加载器;绝大多数的Java程序都会运用这三品种加载器进行加载。 ### 4.1 发起类加载器 这个类由C++完结,担任加载存放在\lib目录,或许被-Xbootclasspath参数所指定的途径中存放的,而且是Java虚拟机可以辨认的(按照文件名辨认,如rt.jar、tools.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机的内存中。发起类加载器无法被Java程序直接引证,用户在编写自定义类加载器时, 假设需求把加载恳求委派给引导类加载器去处理,那直接运用null替代即可。### 4.2 扩展类加载器 这个类加载器是在类sun.misc.Launcher$ExtClassLoader 中以Java代码的方式完结的。它担任加载\lib\ext目录中,或许被java.ext.dirs体系变量所指定的途径中一切的类库。依据“扩展类加载器”这个称号,就可以推断出这是一种Java体系类库的扩展机制,JDK的开发团队容许用户将具有通用性的类库放置在ext目录里以扩展Java SE的功用,在JDK9之后,这种扩展机制被模块化带来的天然的扩展才能所替代。由于扩展类加载器是由Java代码完结的,开发者可以直接在程序中运用扩展类加载器来加载Class文件。### 4.3 运用程序类加载器 这个类加载器由sun.misc.Launcher$AppClassLoader来完结。由于运用程序类加载器是ClassLoader类中的getSystemClassLoader()方法的返回值,所以有些场合中也称它为“体系类加载器”。它担任加载用户类途径(ClassPath)上一切的类库,开发者同样可以直接在代码中运用这个类加载器。假设运用程序中没有自定义过自己的类加载器,一般情况下这个便是程序中默许的类加载器。

    推荐阅读