Skywalking流程分析_6(静态方法的增强流程)

前言

在上文中,介绍了SkyWalkingAgent.Transformer#transform方法,分析了:

  • 找到对此类匹配的所有插件
  • 设置增强上下文标识
  • 字节码增强 define.define
    • 版本查找,类识别和方法识别
      • finder.exist判断是否存在
    • 真正的字节码增强,this.enhance
      接下来我们就来分析真正的字节码增强逻辑

this.enhance

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;
}

这里分为ClassEnhancePluginDefineClassEnhancePluginDefineV2,逻辑都差不多,我们只分析ClassEnhancePluginDefine即可

静态方法增强

ClassEnhancePluginDefine.enhanceClass

protected DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
    ClassLoader classLoader) throws PluginException {
    //获取要拦截的静态方法
    StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
    String enhanceOriginClassName = typeDescription.getTypeName();
    if (staticMethodsInterceptPoints == null || staticMethodsInterceptPoints.length == 0) {
        return newClassBuilder;
    }

    for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
        //获取要在切点进行具体增强逻辑的类
        String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
        if (StringUtil.isEmpty(interceptor)) {
            throw new EnhanceException("no StaticMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
        }
        //如果要修改原方法的入参
        if (staticMethodsInterceptPoint.isOverrideArgs()) {
            //要增加的类是Bootstrap类加载器加载的
            if (isBootstrapInstrumentation()) {
                newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                 .intercept(MethodDelegation.withDefaultConfiguration()
                                                                            .withBinders(Morph.Binder.install(OverrideCallable.class))
                                                                            .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
            } else {
                newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                 .intercept(MethodDelegation.withDefaultConfiguration()
                                                                            .withBinders(Morph.Binder.install(OverrideCallable.class))
                                                                            .to(new StaticMethodsInterWithOverrideArgs(interceptor)));
            }
        //不用修改原方法的入参    
        } else {
            //要增加的类是Bootstrap类加载器加载的
            if (isBootstrapInstrumentation()) {
                newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                 .intercept(MethodDelegation.withDefaultConfiguration()
                                                                            .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
            } else {
                newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                 .intercept(MethodDelegation.withDefaultConfiguration()
                                                                            .to(new StaticMethodsInter(interceptor)));
            }
        }

    }

    return newClassBuilder;
}
  • getStaticMethodsInterceptPoints(),获取静态方法拦截点
  • 通过是否修改原方法入参、要加载的类是否是BootStrapClassLoader加载,来分别执行增强逻辑
不修改原方法入参
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .to(new StaticMethodsInter(interceptor)));

以阿里数据源druid为例,不需要修改方法入参,被拦截的类也不是JDK类库的类,所以直接执行最下面的分支逻辑

public class DruidDataSourceStatManagerInstrumentation extends ClassStaticMethodsEnhancePluginDefine {
    private static final String ENHANCE_CLASS = "com.alibaba.druid.stat.DruidDataSourceStatManager";
    private static final String ENHANCE_METHOD = "addDataSource";
    private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.druid.v1.PoolingAddDruidDataSourceInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }

    //省略...

    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return new StaticMethodsInterceptPoint[]{
                new StaticMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return named(ENHANCE_METHOD).and(takesArguments(Object.class, String.class));
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }

                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                }
        };
    }
}

能明确看到是对com.alibaba.druid.stat.DruidDataSourceStatManager中的addDataSource进行增强的

获取要拦截的增强点后,开始调用bytebuddy提供的API:

  • 指定该方法为静态方法isStatic()
  • 指定方法名staticMethodsInterceptPoint.getMethodsMatcher()
  • 传入interceptor实例交给StaticMethodsInter去处理
  • new StaticMethodsInter(interceptor)去做真正的字节码增强

下面来分析StaticMethodsInter看看具体是怎么做增强的

StaticMethodsInter
public class StaticMethodsInter {
    private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInter.class);

    /**
     * A class full name, and instanceof {@link StaticMethodsAroundInterceptor} This name should only stay in {@link
     * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on
     * books about Classloader or Classloader appointment mechanism.
     * 
     * 进行具体增强逻辑的类
     */
    private String staticMethodsAroundInterceptorClassName;

    /**
     * Set the name of {@link StaticMethodsInter#staticMethodsAroundInterceptorClassName}
     *
     * @param staticMethodsAroundInterceptorClassName class full name.
     */
    public StaticMethodsInter(String staticMethodsAroundInterceptorClassName) {
        this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName;
    }

    /**
     * Intercept the target static method.
     *
     * @param clazz        target class 要修改的字节码的原生类
     * @param allArguments all method arguments 原方法的入参
     * @param method       method description. 原方法
     * @param zuper        the origin call ref. 原方法的调用
     * @return the return value of target static method.
     * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
     *                   bug, if anything triggers this condition ).
     * 开始进行环绕增强                  
     *                   
     */
    @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());

        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());
        }

        Object ret = null;
        try {
            //如果设置不再调用,则返回你指定的结果
            if (!result.isContinue()) {
                ret = result._ret();
            } else {
                //原方法继续调用
                ret = zuper.call();
            }
        } catch (Throwable t) {
            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());
            }
            throw t;
        } finally {
            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());
            }
        }
        return ret;
    }
}

intercept方法总结:

  • 加载实例化自定义的拦截器
  • 执行前置增强beforeMethod()方法
  • 如果需要执行原方法,执行原方法调用,否则调用_ret()方法
  • 如果方法执行抛出异常,执行异常增强handleMethodException()方法
  • 在finally执行后置增强afterMethod()方法

注意看这行

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());
}

Object ret = null;
try {
    //如果设置不再调用,则返回你指定的结果
    if (!result.isContinue()) {
        ret = result._ret();
    } else {
        //原方法继续调用
        ret = zuper.call();
    }
} catch (Throwable t)

MethodInterceptResult结构

public class MethodInterceptResult {
    private boolean isContinue = true;

    private Object ret = null;

    /**
     * define the new return value.
     *
     * @param ret new return value.
     */
    public void defineReturnValue(Object ret) {
        this.isContinue = false;
        this.ret = ret;
    }

    /**
     * @return true, will trigger method interceptor({@link InstMethodsInter} and {@link StaticMethodsInter}) to invoke
     * the origin method. Otherwise, not.
     */
    public boolean isContinue() {
        return isContinue;
    }

    /**
     * @return the new return value.
     */
    public Object _ret() {
        return ret;
    }
}

MethodInterceptResult创建对象result后传入了前置增强beforeMethod中,然后根据result.isContinue()返回的结果判断是否继续调用原方法,也就是说:如果插件的beforeMethod()方法实现中调用了defineReturnValue()传入了返回值,则不会再调用原方法,直接返回传入的返回值

修改原方法入参
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                .withBinders(Morph.Binder.install(OverrideCallable.class))
                                                .to(new StaticMethodsInterWithOverrideArgs(interceptor)));

获取要拦截的增强点后,开始调用bytebuddy提供的API:

  • 指定该方法为静态方法isStatic()
  • 指定方法名staticMethodsInterceptPoint.getMethodsMatcher()
  • 传入interceptor实例交给StaticMethodsInterWithOverrideArgs去处理
  • new StaticMethodsInterWithOverrideArgs(interceptor)去做真正的字节码增强

下面来分析StaticMethodsInterWithOverrideArgs看看具体是怎么做增强的

StaticMethodsInterWithOverrideArgs
public class StaticMethodsInterWithOverrideArgs {
    private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterWithOverrideArgs.class);

    /**
     * A class full name, and instanceof {@link StaticMethodsAroundInterceptor} This name should only stay in {@link
     * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on
     * books about Classloader or Classloader appointment mechanism.
     */
    private String staticMethodsAroundInterceptorClassName;

    /**
     * Set the name of {@link StaticMethodsInterWithOverrideArgs#staticMethodsAroundInterceptorClassName}
     *
     * @param staticMethodsAroundInterceptorClassName class full name.
     */
    public StaticMethodsInterWithOverrideArgs(String staticMethodsAroundInterceptorClassName) {
        this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName;
    }

    /**
     * Intercept the target static method.
     *
     * @param clazz        target class
     * @param allArguments all method arguments
     * @param method       method description.
     * @param zuper        the origin call ref.
     * @return the return value of target static method.
     * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
     *                   bug, if anything triggers this condition ).
     */
    @RuntimeType
    public Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
        @Morph OverrideCallable zuper) throws Throwable {
        StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz
            .getClassLoader());

        MethodInterceptResult result = new MethodInterceptResult();
        try {
            //beforeMethod可以通过操作allArguments来修改原方法入参
            interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
        } catch (Throwable t) {
            LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
        }

        Object ret = null;
        try {
            if (!result.isContinue()) {
                ret = result._ret();
            } else {
                // 原方法的调用时传入修改后的原方法入参
                ret = zuper.call(allArguments);
            }
        } catch (Throwable t) {
            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());
            }
            throw t;
        } finally {
            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());
            }
        }
        return ret;
    }
}

其实能看到StaticMethodsInter和StaticMethodsInterWithOverrideArgs的区别就在于执行原方法调用是的方法不同:

Callable<?> zuper

ret = zuper.call();
OverrideCallable zuper

ret = zuper.call(allArguments);

静态方法增强的总结:

  • 不修改原方法入参
    • 要增强的类是JDK的类库(待分析)
    • 要增强的类不是JDK的类库
      • 实例化插件中定义的Interceptor
      • 执行前置增强beforeMethod
      • 执行原方法
      • 如果出现异常,执行异常增强handleMethodException
      • 执行后置增强afterMethod
  • 要修改原方法入参
    • 要增强的类是JDK的类库(待分析)
    • 要增强的类不是JDK的类库
      • 实例化插件中定义的Interceptor
      • 执行前置增强beforeMethod
      • 执行原方法
      • 如果出现异常,执行异常增强handleMethodException
      • 执行后置增强afterMethod

静态方法的具体增强逻辑我们已经分析完毕,后面的文章会分析构造/实例方法增强,以及JDK类库的类是怎么增强的

你可能感兴趣的:(Skywalking,skywalking)