2.1 如何使用插件增强类
2025/9/5大约 5 分钟
2.1 如何使用插件增强类
接上文 客户端的启动流程
插件定义对目标类进行增强
基本流程就是下面这样,分为两步:
- 为当前类匹配插件定义
- 使用插件定义来对当前类进行增强
代码删除了部分逻辑,只保留了逻辑代码
@Override
public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder,
final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule javaModule,
final ProtectionDomain protectionDomain) {
// 1.传入要被增强的类,查找所有匹配的插件定义
List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
// 2.增强逻辑
DynamicType.Builder<?> possibleNewBuilder = define.define(
typeDescription, newBuilder, classLoader, context);
}
return newBuilder;
}
可以看到,增强规则的核心就是插件定义的define
函数。
增强实现
代码入口
org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine#define
大概流程分为以下几点:
- 特征名单检查
- 增强类
- 增强静态函数
- 增强实例函数(包含对象函数和构造函数)
public DynamicType.Builder<?> define(TypeDescription typeDescription, DynamicType.Builder<?> builder,
ClassLoader classLoader, EnhanceContext context) throws PluginException {
// 获取拦截器类名 --- 也就是说当前插件的类名
String interceptorDefineClassName = this.getClass().getName();
// 被处理的Java类名
String transformClassName = typeDescription.getTypeName();
WitnessFinder finder = WitnessFinder.INSTANCE;
/**
* find witness classes for enhance class
* 区分不同的版本的类的全路径类名,比如:
* 对于 工具 a 他有两个版本 V1.0 和 V2.0,他们的类名是一样的,但是方法名不一样,这个时候就需要用到 witness classes
* 即同一个工具,需要增强的位置改变了,如何来确定?就通过这个来确定使用哪个插件来增强哪个版本的工具A ?
* 比如说 v2 有类名 tool2
* v1 有类名 tool1
* 那么就可以通过这两个东西来区分当前使用 工具a 的版本是 v1 还是 v2
* 从而决定使用哪个版本的插件来进行增强。不匹配的插件就不会生效。
*/
String[] witnessClasses = witnessClasses();
if (witnessClasses != null) {
for (String witnessClass : witnessClasses) {
if (!finder.exist(witnessClass, classLoader)) {
LOGGER.warn("enhance class {} by plugin {} is not activated. Witness class {} does not exist.", transformClassName, interceptorDefineClassName, witnessClass);
return null; // 返回null 说明当前插件无法增强当前类。需要其他版本的插件来增强
}
}
}
// 函数匹配,跟上面的大概意思是一样的
List<WitnessMethod> witnessMethods = witnessMethods();
if (!CollectionUtil.isEmpty(witnessMethods)) {
for (WitnessMethod witnessMethod : witnessMethods) {
if (!finder.exist(witnessMethod, classLoader)) {
LOGGER.warn("enhance class {} by plugin {} is not activated. Witness method {} does not exist.", transformClassName, interceptorDefineClassName, witnessMethod);
return null;
}
}
}
/**
* find origin class source code for interceptor
*
* 这里就是要被增强的Java类、使用当前插件来增强这个类 typeDescription
* 这里说的是增强的定义、即先告诉bytebuddy 如何进行增强,还不进行实际的增强操作、
* 最后的增强操作,在installOn的时候完成。
*
* 这里的增强定义包括:
* 1. 增强静态函数的定义
* 2. 增强实体函数和构造器的定义
*/
DynamicType.Builder<?> newClassBuilder = this.enhance(typeDescription, builder, classLoader, context);
context.initializationStageCompleted(); // 标志初始化完成
return newClassBuilder;
}
protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
ClassLoader classLoader, EnhanceContext context) throws PluginException {
// 增强静态函数的定义
newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
// 增强实体函数和构造器的定义
newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);
return newClassBuilder;
}
增强静态类 V1 版本
函数入口:org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine#enhanceClass
简单概括:
- 找到当前def 的静态函数拦截点
- 遍历这些拦截点,将静态函数的执行委托给
StaticMethodsInter
去执行 - 而
StaticMethodInter
的org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsInter#intercept
又将实际的执行委托给了插件去执行,同时还实现 aop的效果
类增强
protected DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
ClassLoader classLoader) throws PluginException {
// 获取def 类中的拦截点 ,这个拦截点由插件作者填写,即 处理哪些函数,使用哪个插件来增强,都再这里写着
StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
String enhanceOriginClassName = typeDescription.getTypeName();
// 处理静态函数拦截点
for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
// 拦截器 类名
String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
// 实际增强逻辑
// 将静态函数委托给 StaticMethodsInter 去处理
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
.intercept(MethodDelegation.withDefaultConfiguration()
.to(new StaticMethodsInter(getPluginName(), interceptor), delegateNamingResolver.resolve(staticMethodsInterceptPoint)));
}
return newClassBuilder;
}
委托类执行org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsInter#intercept
概述:
实际执行分成几步:
- 找到实际的插件类
- 将实际的执行委托给插件去处理
- 前置拦截
- 执行被增强的原始函数
- 异常拦截
- 后置拦截
这几个拦截函数,我们在写插件的时候,是一个接口,可以通过在这里写自己的逻辑。
@RuntimeType
public Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
@SuperCall Callable<?> zuper) throws Throwable {
// 加载插件对象
StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz
.getClassLoader());
long interceptorTimeCost = 0L;
long startTimeOfMethodBeforeInter = System.nanoTime();
MethodInterceptResult result = new MethodInterceptResult();
try {
// 前置执行
interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
} catch (Throwable t) {
LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE);
}
interceptorTimeCost += System.nanoTime() - startTimeOfMethodBeforeInter;
Object ret = null;
try {
// 实际执行目标函数,注意不是插件函数,是被增强的类的函数
if (!result.isContinue()) {
ret = result._ret();
} else {
ret = zuper.call();
}
} catch (Throwable t) {
long startTimeOfMethodHandleExceptionInter = System.nanoTime();
try {
// 异常拦截
interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
} catch (Throwable t2) {
LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE);
}
interceptorTimeCost += System.nanoTime() - startTimeOfMethodHandleExceptionInter;
throw t;
} finally {
long startTimeOfMethodAfterInter = System.nanoTime();
try {
// 后置拦截
ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
} catch (Throwable t) {
LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
AgentSo11y.errorOfPlugin(pluginName, INTERCEPTOR_TYPE);
}
interceptorTimeCost += System.nanoTime() - startTimeOfMethodAfterInter;
}
AgentSo11y.durationOfInterceptor(interceptorTimeCost);
return ret;
}
静态函数插件定义
public interface StaticMethodsAroundInterceptor {
// 前置拦截函数
void beforeMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
MethodInterceptResult result);
// 后置拦截函数
Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Object ret);
// 异常拦截函数
void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
Throwable t);
}
随便找一个插件演示一下
以这个插件来解释一下,
- beforeMethod中记录span
- afterMethod 停止span
- handleMethodException: 记录错误日志
public class SpringAnnotationInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
String operationName = objInst.getClass().getName() + "." + method.getName();
AbstractSpan span = ContextManager.createLocalSpan(operationName);
span.setComponent(ComponentsDefine.SPRING_ANNOTATION);
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
ContextManager.stopSpan();
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
ContextManager.activeSpan().log(t);
}
}