使用 Byte Buddy 创建自定义方法签名的高效方法

Performant way of creating a custom method signature with Byte Buddy

我正在尝试使用 ByteBuddy 实现 Profiler。我目前正在努力有效地为我正在分析的方法创建一个正确的签名。

这是我当前实施的要点:https://gist.github.com/felixbarny/e0c64819c59368a28200

ProfilingInterceptor.profile 方法有两种实现方式。每个人都有自己的缺点。

第一个使用@Origin String signature作为签名。这非常有效,因为 ByteBuddy 似乎缓存了它。问题是,我对签名的格式不满意。例如 method2(I)I

在第二个实现中,我注入 @Origin(cacheMethod = true) Method method 并手动构建一个更好看的签名:int org.stagemonitor.benchmark.profiler.ClassJavassistProfiled.method2(int)。明显的问题是每次调用都会重新创建签名 - 性能不佳(我的 jmh 基准测试说它慢了 4 倍)。

有没有办法缓存签名,例如为每个签名创建一个字符串常量?

提前致谢

实现您想要的最简单方法是实现您自己的注释并创建绑定。然后将任何此类 String 值添加到 class 的常量池并从那里返回。

您可以在 Byte Buddy 教程的底部找到 example for creating a custom annotation。要实现自定义 @Signature 注释,您需要执行以下操作:

enum SignatureBinder
    implements TargetMethodAnnotationDrivenBinder.ParameterBinder<StringValue> {

  INSTANCE; // singleton

  @Override
  public Class<Signature> getHandledType() {
    return Signature.class;
  }

  @Override
  public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loaded<StringValue> annotation,
      MethodDescription source,
      ParameterDescription target,
      Instrumentation.Target instrumentationTarget,
      Assigner assigner) {
    if (!target.getTypeDescription().represents(String.class)) {
      throw new IllegalStateException(target + " makes illegal use of @Signature");
    }
    StackManipulation constant = new TextConstant("<signature goes here>");
    return new MethodDelegationBinder.ParameterBinding.Anonymous(constant);
  }
}

名为 sourceMethodDescription 对象描述了拦截的方法并提供了类似于 Method class 的接口。然后,您将使用 MethodDelegation 注册此活页夹,然后您可以在代理中使用注释。

附带说明:我会避免使用规范名称。这些名称可能会相互冲突,例如classes

foo.Bar$Qux
foo.Bar.Qux

with Bar 曾经是 class 和内部 class Qux 并且曾经是 class Qux 的包裹同名。我知道这不太可能,但你永远不知道用户代码会是什么样子。