3

我对这里描述的方法委托很感兴趣:

http://www.javacodegeeks.com/2015/01/make-agents-not-frameworks.html

这很好用:

.intercept(MethodDelegation.to(LogInterceptor.class)
                              .andThen(SuperMethodCall.INSTANCE)

我可以拦截调用并捕获传递给方法的参数,这是我想要实现的一半。但是,我还没有找到一种同样简洁的方法来捕获返回值。我知道我可以将 Callable 传递给执行调用的拦截器,但是沿着这条路走下去似乎是弄乱我的堆栈跟踪的可靠方法。

在我看来,应该有一种简单且规范的方式来实现“环绕方法”模式。

在我开始深入研究实数 API 之前:我错过了什么吗?

4

1 回答 1

2

不,你没有错过任何东西。

每当您使用 Byte Buddy 操作代码时,此操作将反映在应用程序的堆栈跟踪中。这是故意的,因为它使调试更容易,以防出现问题。想想你的日志拦截器抛出运行时异常;如果拦截以某种方式合并到您的原始方法中,那么其他开发人员很难弄清楚。使用 Byte Buddy 的方法,您可以简单地导航到导致源,因为您的拦截器实际上可以从那里获得。使用 Byte Buddy,生成的代码不会引发异常,因此任何问题都可以追溯到源代码。

此外,合并堆栈帧可能会对调用者敏感代码产生奇怪的副作用。例如,安全管理员可能会授予拦截器比截获代码更高的权限。合并堆栈帧将恢复这些权限。

编写带有@Super Callable注入的拦截器是实现 arround-advice 的规范方法。也不用担心性能。Byte Buddy 的编写方式使得 JIT 编译器很容易内联代码,这样超级方法调用很可能以零开销执行。(甚至有一个基准证明了这一点。)对于您的示例,通用 arround-advce 如下所示:

public class TimingInterceptor {

  @RuntimeType
  public static Object intercept(@Super Callable<?> zuper) 
      throws Exception {
    long before = System.currentTimeMillis();
    try {
      return zuper.call();
    } finally {
      System.out.println("Took: " + (Systen.currentTimeMillis() - before));
    }
  }
}

对于每个方法,执行所需的时间现在都打印到控制台。您使用MethodDelegation.to(TimingInterceptor.class).

确保使用@RuntimeType注释。这样,Byte Buddy 在运行时尝试进行强制转换,从而使这种通用拦截成为可能。

于 2015-11-17T07:04:24.140 回答