开启生长之旅!这是我参加「日新方案 12 月更文挑战」的第 23 天,点击查看活动详情
我是石页兄,本篇不带情感,只聊干货
欢迎重视微信大众号「架构染色」沟通和学习
一、背景
昨晚开端发烧、肌肉酸痛、无力,导致昨天的日更都断掉了,今日核酸结果还没出来,不能去公司,而身体的状况也无法支撑在公司坐一天,不知道自己是不是阳了。趴在床上用枕头给身体做好支撑,把今日的日更完成。早些天有个朋友在看到《当月亮看护地球 | SkyWalking Agent 看护你的运用…有它相伴才闲适》时,有对其中一些文字表示有贰言。
朋友:在我知识体系中ClassLoader
的双亲派遣机制是流通丝滑的,可是看到经过派遣履行类加载来保障这种分治能力,从而到达了类资源的阻隔性突然就感觉有点陌生和排斥呢?
我:类的命名空间有了解嘛?
朋友:你是说package
嘛?
我:我说的是ClassLoader
中的 nameSpace
朋友:啥玩意儿?
本篇是对ClassLoader
中的namespace
做个直观的介绍和验证。这个知识点笔者个人认为很重要,屡次协助笔者解决日常工作中遇到的疑难杂症,假如你没有仔细研讨过ClassLoader
,但懵懂的认知让你觉得这个应该很简单,那请看下图,若看不懂则标明你可能并不了解ClassLoader
中得一些关键逻辑;不贩卖焦虑,不感兴趣则疏忽,知道个大概即可,不会它并不影响你做一个优异的程序员。
namespace(图片来自网络).png
二、Classloader的分治和派遣机制
上下文同步,已了解这部分的读者朋友可直接越过,进过第三末节。
ClassLoader
是个抽象类,其子类UrlClassLoader
引进URL
资源概念,以此方法来约束自己只加载URL
掩盖范围内的类文件;ExtClassLoader
、AppClassLoader
都是UrlClassLoader
的子类,各自分管的资源路径不同,即给定的URL
不同则统辖范围不同,并经过派遣履行类加载来保障这种分治能力,从而到达了类资源的阻隔性。
上图是标准的派遣机制,总结为2个方面:
-
父加载器能加载父加载器来加载:
- 自己在加载资源之前,先让父类加载器去加载。父类再找其父类,直到
BootStrapClassLoader
(它没有父类加载器)。 - 确保了等级越高,加载的优先权越高
- 自己在加载资源之前,先让父类加载器去加载。父类再找其父类,直到
-
父加载器不加载我就来加载(findClass);我加载不了的子加载器来加载:
- 若父类加载器没有加载成功,才逐级下放这个加载权。
- 子类加载器不能加载父类加载器能加载的类,如
java.lang.String
,即便用户自己编造一份这个类型,发动类加载器优先将java.lang.String
加载成功后,运用类加载器就不会再加载用户自己编造的。
下图描绘了SkyWalkingAgent经过自定义的类加载器AgentClassLoader
加载插件中的类,不会对宿主运用中的类产生污染。
三、namespace
是什么?
每个类加载器都对应一个namespace
,汉化叫命名空间(我个人其实更喜欢汉化为名字空间),命名空间由该加载器及所有父类加载器所加载的类组成。这样的介绍很抽象,网络资猜中也多是这么几句话,我会从更多的细粒度视角带您进一步了解它。
3.1 同一个命名空间中,类只加载一份
比如AppClassLoader
加载程序中编写的类。无论加载多少次,只要是被AppClassLoader
加载的,其Class
信息的hashcode
都是相同的。
3.2 子加载器可见父加载器加载的类
这个更容易举例,核心类库的类由BootStrapClassLoder
加载的,比如String
;由AppClassLoader
所加载的类,都能运用String
对吧?
3.3 父加载器不可见子加载器所加载的类
SPI
技术的诞生也是这个原因,什么是SPI
:程序运行过程中要用到的类,经过当时类加载器
的主动加载
,加载不到(不在当时类加载器的类资源统辖范围),假如要运用这个类,有必要指定一个能够加载这个类的加载器去加载,而怎样获取这个加载器是个问题。
程序都是在线程中履行,那么从线程的上下文中去拿最合理,所以就诞生了线程上下文类加载器,这中场景下加载器就得采用非主动加载
,即经过 forName
或许 loadClass
的方法去加载类。
下边经过示例+科技来验证加载不到的情况
示例中有Parent
类,Son
类。Parent
类中有个getSon
办法中,会运用到Son
类。
编译后,在classpath
下,把Son
类移到自定义子加载器的资源目录中,让AppClassLoader
无法加载。到达我们的运行环境要求:
-
Parent
由AppClassLoader
加载 -
Son
由自定义的子加载器加载. - 调用
Parent
的getSon
办法的时分要new Son()
,这样会触发Son
类的加载,可是加载不到Son
类,报错信息为:java.lang.NoClassDefFoundError: com/rock/Son
public class Parent {
private Object son;
public Object getSon(){
return new Son();
}
public Object setSon(Object son){
this.son = son;
return this.son;
}
}
public class Son {
}
/**
* 父加载器加载不了,子加载器所加载的类,
* 父加载器加载Parent
* 子加载器加载son
* Parent#getSon 办法里 new Son()目标.//报错,找不到Son的类定义.
*/
@Test
public void testParentCanntFindSon(){
CustomClassLoader01 customClassLoader01 = new CustomClassLoader01(ClassLoader.getSystemClassLoader());
try {
Class<?> classParent = customClassLoader01.loadClass("com.rock.Parent");
System.out.println("classParent:" + classParent.getClassLoader());
Class<?> classSon = customClassLoader01.loadClass("com.rock.Son");
System.out.println("classSon:" + classSon.getClassLoader());
Object objParent = classParent.newInstance();
Object objSon = classSon.newInstance();
Method setSon = classParent.getMethod("setSon",Object.class);
Object resultSon = setSon.invoke(objParent, objSon);
System.out.println(resultSon.getClass());
System.out.println(resultSon.getClass().getClassLoader());
try {
Method getSon = classParent.getMethod("getSon");
Object invoke = getSon.invoke(objParent);
System.out.println(invoke.getClass().getClassLoader());
}catch (Exception exp){
//java.lang.NoClassDefFoundError: com/rock/Son
exp.printStackTrace();
}
}catch (Exception exp){
exp.printStackTrace();
}
}
3.4 不同命名空间的类相互不可见
这个特征也经过实例来验证一下,自定义类加载器 new
出来两个实例,每个实例各自加载一次Man
类,经过newInstance
方法实例化出两个目标,目标之间相互调用setFather
赋值就会报错。即a空间的不识别b空间的类,即便全限制名相同也不识别。这种特性所导致的坑你踩到过嘛?
public class Man {
private Man father;
public void setFather(Object obj){
father = (Man)obj;
}
}
@Test
public void testSifferentNamespaceClass(){
CustomClassLoader01 customClassLoader01 = new CustomClassLoader01(ClassLoader.getSystemClassLoader());
CustomClassLoader01 customClassLoader02 = new CustomClassLoader01(ClassLoader.getSystemClassLoader());
try {
Class<?> aClass1 = customClassLoader01.loadClass("com.rock.Man");
System.out.println("class1 : " + aClass1.getClassLoader());
System.out.println("class1 : " + aClass1.);
Class<?> aClass2 = customClassLoader02.loadClass("com.rock.Man");
System.out.println("class2 : " + aClass1.getClassLoader());
System.out.println("class2 : " + aClass2);
Object man1 = aClass1.newInstance();
Object man2 = aClass2.newInstance();
Method setFather = aClass1.getMethod("setFather", Object.class);
setFather.invoke(man1,man2);
}catch (Exception exp){
exp.printStackTrace();
}
}
class1 : com.rock.classLoader.CustomClassLoader01@1f28c152
class1 : 2006034581
class2 : com.rock.classLoader.CustomClassLoader01@1f28c152
class2 : 488044861
...
Caused by: java.lang.ClassCastException: com.rock.Man cannot be cast to com.rock.Man
at com.rock.Man.setFather(Man.java:6)
... 27 more
四、最后说一句
我是石页兄,假如这篇文章对您有协助,或许有所启发的话,欢迎重视笔者的微信大众号【 架构染色 】进行沟通和学习。您的支持是我坚持写作最大的动力。
欢迎点击链接扫马儿重视、沟通。