1 ContainerClassLoader
com.alipay.sofa.ark.bootstrap.ContainerClassLoader 直接继承URLClassLoader
2 PluginClassLoader
com.alipay.sofa.ark.container.service.classloader.PluginClassLoader
// 0. sun reflect related class throw exception directly
if (classloaderService.isSunReflectClass(name)) {
throw new ArkLoaderException(
String
.format(
"[ArkPlugin Loader] %s : can not load class: %s, this class can only be loaded by sun.reflect.DelegatingClassLoader",
pluginName, name));
}
// 1. findLoadedClass
if (clazz == null) {
clazz = findLoadedClass(name);
}
// 2. JDK related class
if (clazz == null) {
clazz = resolveJDKClass(name);
}
// 3. Ark Spi class
if (clazz == null) {
clazz = resolveArkClass(name);
}
// 4. pre find class
if (clazz == null) {
clazz = preLoadClass(name);
}
// 5. Import class export by other plugins
if (clazz == null) {
clazz = resolveExportClass(name);
}
// 6. Plugin classpath class
if (clazz == null) {
clazz = resolveLocalClass(name);
}
// 7. Java Agent ClassLoader for agent problem
if (clazz == null) {
clazz = resolveJavaAgentClass(name);
}
// 8. Post find class
if (clazz == null) {
clazz = postLoadClass(name);
}
if (clazz != null) {
if (resolve) {
super.resolveClass(clazz);
}
return clazz;
}
throw new ArkLoaderException(String.format(
"[ArkPlugin Loader] %s : can not load class: %s", pluginName, name));
每个 Ark 插件都拥有一个独立的类加载器,其类加载的顺序如下:
1 如果是加载反射生成的字节码,那么会直接抛出 ClassNotFoundException,终止类加载。这一部分主要是来源于我们的工程实践,避免一定找不到的类查找路径过长
2 查找已经被加载过的类
查找 JDK 中的类,这一块主要包含两部分:第一部分是 ExtClassloader 负责加载的类;第二部分是 JDK 提供的类但从 ExtClassloader 中加载不到,但在本地运行时会被加入到 SystemClassloader 的 classpath 中,同时这些类可能会被放到一些三方工具包中,典型的如 tool.jar 中 sun.tools.attach.BsdVirtualMachine,这一部分也主要来源于我们的工程实践,避免类被加载超过一次,从而引发报错
3 看类是否是由 Sofa Ark 提供的接口类,典型的如: com.alipay.sofa.ark.spi.service.PluginActivator, 如果是,则类会委托给 Ark 容器的类加载器加载
4 看是否在插件的 import 中(包括 import-classes 和 import-package), 如果在,则会委托给导出该类的插件类加载器加载在插件自身的 classpath 中加载
如果上述都失败了,则会尝试在 SymtemClassloader 中加载,这一步主要是为了解决使用 agent 时的情形
3 BizClassLoader
com.alipay.sofa.ark.container.service.classloader.BizClassLoader:
// 0. sun reflect related class throw exception directly
if (classloaderService.isSunReflectClass(name)) {
throw new ArkLoaderException(
String
.format(
"[ArkBiz Loader] %s : can not load class: %s, this class can only be loaded by sun.reflect.DelegatingClassLoader",
bizIdentity, name));
}
// 1. findLoadedClass
if (clazz == null) {
clazz = findLoadedClass(name);
}
// 2. JDK related class
if (clazz == null) {
clazz = resolveJDKClass(name);
}
// 3. Ark Spi class
if (clazz == null) {
clazz = resolveArkClass(name);
}
// 4. pre find class
if (clazz == null) {
clazz = preLoadClass(name);
}
// 5. Plugin Export class
if (clazz == null) {
clazz = resolveExportClass(name);
}
// 6. Biz classpath class
if (clazz == null) {
clazz = resolveLocalClass(name);
}
// 7. Java Agent ClassLoader for agent problem
if (clazz == null) {
clazz = resolveJavaAgentClass(name);
}
// 8. post find class
if (clazz == null) {
clazz = postLoadClass(name);
}
if (clazz != null) {
if (resolve) {
super.resolveClass(clazz);
}
return clazz;
}
throw new ArkLoaderException(String.format("[ArkBiz Loader] %s : can not load class: %s",
bizIdentity, name));
下步骤搜索:
- 如果已加载过,那就返回已加载好的那个类。
- 如果这个类是JDK自己的,那么就用JDKClassLoader去加载。
- 如果这个类是属于Ark容器的,那么就用ArkClassLoader去加载。
- 如果这个类是某个插件export的,那么就用ExportClassLoader去加载。
- 如果这个类是我业务自己的,那么就用当前的ClassLoader直接loadClass就好。
- 否则就去试试是不是用了某个java agent。
- 再找不到就报错。