本文基于 SkyWalking-Java-agent 8.15.0 版本
// 2.加载插件
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
PluginBootstrap 类里面只有一个方法,用于加载所有的插件
插件加载核心方法:loadPlugins()
/**
* load all plugins.
*
* @return plugin definition list.
*/
public List<AbstractClassEnhancePluginDefine> loadPlugins() throws AgentPackageNotFoundException {
// 初始化自定义的类加载器
AgentClassLoader.initDefaultLoader();
PluginResourcesResolver resolver = new PluginResourcesResolver();
// 1)拿到所有skywalking-plugin.def的资源
List<URL> resources = resolver.getResources();
if (resources == null || resources.size() == 0) {
LOGGER.info("no plugin files (skywalking-plugin.def) found, continue to start application.");
return new ArrayList<AbstractClassEnhancePluginDefine>();
}
// 2)遍历
for (URL pluginUrl : resources) {
try {
PluginCfg.INSTANCE.load(pluginUrl.openStream());
} catch (Throwable t) {
LOGGER.error(t, "plugin file [{}] init failure.", pluginUrl);
}
}
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();
for (PluginDefine pluginDefine : pluginClassList) {
try {
LOGGER.debug("loading plugin class {}.", pluginDefine.getDefineClass());
// 3)通过AgentClassLoader 实例化插件定义类实例
AbstractClassEnhancePluginDefine plugin = (AbstractClassEnhancePluginDefine) Class.forName(pluginDefine.getDefineClass(), true, AgentClassLoader
.getDefault()).newInstance();
plugins.add(plugin);
} catch (Throwable t) {
LOGGER.error(t, "load plugin [{}] failure.", pluginDefine.getDefineClass());
}
}
// 4)加载基于xml定义的插件
plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
return plugins;
}
插件加载的体系结构包含以下重要的知识点:
并行类加载器
// 初始化自定义的类加载器
AgentClassLoader.initDefaultLoader();
public class AgentClassLoader extends ClassLoader
AgentClassLoader 继承自 JDK 的 java.lang.ClassLoader,是 SkyWalking 自定义的类加载器,负责查找插件和拦截器。在该类的 static代码块里使用 registerAsParallelCapable 方法,将类加载器注册为具备并行能力。
类加载器注册为具备并行能力,需要同时满足两个条件,注册才能成功:
- 调用该方法的类加载器实例还没有创建
- 调用该方法的类加载器所有父类(Object类除外)都注册为具备并行能力
注意:一旦类加载器被注册为并行能力,就不能被改变回串行能力
- 默认情况下,类加载器以自身为锁,串行加载类
- 先将类注册为并行能力,可以实现类的并行加载。并行加载的实现方式是在
parallelLockMap里面存放加载类相关的锁信息,类加载过程的锁粒度细化到类级别
// 获取类加载时的锁
protected Object getClassLoadingLock(String className) {
Object lock = this;
// 并行锁
// 并行锁不存在时,默认使用类加载器所谓锁
if (parallelLockMap != null) {
// 并行锁存在时,以加载类区分不同的锁
Object newLock = new Object();
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
/**
* The default class loader for the agent.
*/
private static AgentClassLoader DEFAULT_LOADER;
private List<File> classpath;
private List<Jar> allJars;
private ReentrantLock jarScanLock = new ReentrantLock();
public static AgentClassLoader getDefault() {
return DEFAULT_LOADER;
}
/**
* Init the default class loader.
* 初始化默认类加载器
*
* @throws AgentPackageNotFoundException if agent package is not found.
*/
public static void initDefaultLoader() throws AgentPackageNotFoundException {
if (DEFAULT_LOADER == null) {
synchronized (AgentClassLoader.class) {
if (DEFAULT_LOADER == null) {
DEFAULT_LOADER = new AgentClassLoader(PluginBootstrap.class.getClassLoader());
}
}
}
}
public AgentClassLoader(ClassLoader parent) throws AgentPackageNotFoundException {
super(parent);
// SkyWalking agent.jar 文件的相对路径,该路径是必需的,用于定位插件和工具
File agentDictionary = AgentPackagePath.getPath();
classpath = new LinkedList<>();
// Config.Plugin.MOUNT 挂载插件文件夹。文件夹路径是相对于agent.jar的
// Config.Plugin.MOUNT 的定义:public static List<String> MOUNT = Arrays.asList("plugins", "activations")
Config.Plugin.MOUNT.forEach(mountFolder -> classpath.add(new File(agentDictionary, mountFolder)));
}
插件定义体系
插件定义
拦截实例方法/构造器
抽象类 ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine,子类
public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
/**
* @return null, means enhance no static methods.
*/
@Override
public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
return null;
}
}
拦截静态方法
ClassStaticMethodsEnhancePluginDefine
插件定义
AbstractClassEnhancePluginDefine 是所有插件定义的顶级父类
- 要拦截的类:enhanceClass()
- 要拦截的方法:getXxxInterceptPoints()
目标类匹配
ClassMatch
- 按类名匹配:NameMatch
- 间接匹配:IndirectMatch
2.1. PrefixMatch
2.2. MethodAnnotationMatch
拦截器定义
- beforeMethod
- afterMethod
- handleMethodException
插件声明
插件声明的文件:resources/skywalking-plugin.def
插件名称 = 插件定义
插件加载
实例化所有插件
PluginBootstrap
- PluginResourcesResolver 查找 skywalking-plugin.def
- PluginCfg 封装 PluginDefine
- DynamicPluginLoader 加载基于 xml 配置的插件
分类插件
PluginFinder
- 命名插件-NameMatch
- 间接匹配插件-IndirectMatch
- JDK 类库插件