Jarslink源码解析-----ModuleClassLoader
ModuleClassLoader是jarslink自定义的类加载器,继承自URLClassLoader,同时可以强制指定一些包下的class,由本ClassLoader自己加载,不通过父ClassLoader加载,突破双亲委派机制。
上面这句话是源码中作者为我们说明的,接下来我将借助源码及之前博客中的知识说明一下这个类主要涉及的点。
我们可以从代码中看到,ModuleClassLoader继承自URLClassLoader,该类也是系统类加载器和扩展类加载器的父类(此处是父类,而不是父类加载器,这里是类与类之间的继承关系),URLClassLoader功能比较强大,它既可以从本地文件系统获取二进制文件来加载类,也可以从远程主机获取二进制文件来加载类。
其实我们可以将URLClassLoader理解为ClassLoader的扩展,封装了大部分的获取类的操作,他可以从以下几种形式加载类:
- 文件: (从文件系统目录加载)
- jar包: (从Jar包进行加载)
- Http: (从远程的Http服务进行加载
并且,URLClassLoader还为Jarslink提供了一个动态加载中最重要的一个点,就是URLClassLoader实现了一个重要功能就是对ClassLoader的close,该功能是JDK 1.7中新增的功能。当class文件或者resources资源文件更新后,我们需要重新加载这些类或者Jar。从理论上来说,当应用清理了对所加载的对象的引用,那么垃圾收集器就会将这些对象给收集掉,然后我们再重新加载新的JAR文件,并创建一个新的URLClassLoader来加载。可是这里有一个问题,就是我们不知道垃圾收集器什么时候将那些未被引用的对象给收集掉,特别是在Windows中,因为在Windows中打开的文件是不可以被删除或被替换的。
jarslink目前官方版本中存在的一个问题就是每个模块卸载后的ClassLoader没有close,导致模块加载过的jar包一直占用,该情况在windows极为明显。
接下来我们来看一下jarslink是如何突破jvm的“双亲委派”的。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先检查是否已经加载过该类
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) {
// 如果仍然没有加载到该类,则调用findclass方法加载
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;
}
}
上面这段代码是ClassLoader中原有的loadClass方法,我们可以看到逻辑,若启动类加载器和父类加载器都没有加载到该类时,classLoader会调用findClass方法尝试使用当前类加载器加载,所以我们大概可以了解到若想实现我们的自定义类加载,就必须重写loadClass和findClass方法。
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> result = null;
synchronized (ModuleClassLoader.class) {
if (isEligibleForOverriding(name)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Load class for overriding: {}", name);
}
result = loadClassForOverriding(name, resolve);
}
if (result != null) {
//链接类
if (resolve) {
resolveClass(result);
}
return result;
}
}
//使用默认类加载方式
return super.loadClass(name, resolve);
}
上面是ModuleClassLoader中的loadClass方法,可以看到,jarslink为了突破双亲委派机制,首先并没有尝试去按照classloader中的逻辑寻找加载,而是先通过判断该类是否要突破双亲委派,如果是的话,则调用类中的loadClassForOverriding方法
private Class<?> loadClassForOverriding(String name, boolean resolve) throws ClassNotFoundException {
//查找已加载的类
Class<?> result = findLoadedClass(name);
if (result == null) {
//加载类
try {
result = findClass(name);
} catch (ClassNotFoundException e) {
//当前子模块系统加载不到时尝试从父容器中加载
if (LOGGER.isInfoEnabled()) {
LOGGER.info("class will load from parent context: {}", name);
}
result = super.loadClass(name, resolve);
}
}
return result;
}
可以看到最终调用了findClass方法来加载类,而继承的URLClassloader已经实现了该方法,所以进需要调用即可。
本文地址:https://blog.csdn.net/bigdogLIU/article/details/112596219
推荐阅读
-
源码解析Spring 数据库异常抽理知识点总结
-
.NET Core源码解析配置文件及依赖注入
-
jQuery 源码解析(七) jQuery对象和DOM对象的互相转换
-
自助建站系统源码解析 教你辨别真正好用的建站系统
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
-
AngularJS动态生成div的ID源码解析
-
java源码解析(java新手代码大全)
-
ArrayList构造方法的源码解析
-
Spring MVC源码(三) ----- @RequestBody和@ResponseBody原理解析
-
基于Spring注解的上下文初始化过程源码解析(一)