我对这里描述的方法委派非常有趣:
http://www.javacodegeeks.com/2015/01/make-agents-not-frameworks.html
这很好用:
.intercept(MethodDelegation.to(LogInterceptor.class)
.andThen(SuperMethodCall.INSTANCE)
我可以拦截调用并捕获传递给方法的参数,这是我想要实现的一半。但是,我还没有找到一种同样简洁的方法来捕获返回值。我知道我可以将一个Callable传递给执行调用的拦截器,但沿着这条路走下去似乎是一个弄乱我的堆栈跟踪的可靠方法。
在我看来,应该有一种简单而规范的方式来实现“around-method”模式。
在我开始研究用于实际的API之前:我错过了什么吗?
答案 0 :(得分:2)
不,你没有遗漏任何东西。
每当您使用Byte Buddy操作代码时,此操作将由应用程序的堆栈跟踪反映出来。这是故意的,因为它可以在出现问题时更轻松地进行调试。想想您的日志拦截器抛出运行时异常;如果拦截以某种方式合并到您的原始方法中,这对于其他开发人员来说是非常困惑的。使用Byte Buddy的方法,您可以直接导航到导致源,因为您的拦截器实际上可以从那里获得。使用Byte Buddy,生成的代码不会抛出异常,因此任何问题都可以追溯到源代码。
此外,合并堆栈帧可能会对调用者敏感代码产生奇怪的副作用。例如,安全管理器可能会为拦截器提供比截取的代码更高的权限。合并堆栈帧将恢复这些权限。
编写一个注入了@Super Callable
的拦截器是实现arround-advice的规范方法。不要担心性能。 Byte Buddy的编写方式使JIT编译器很容易内联代码,以便超级方法调用最有可能以零开销执行。 (甚至有a benchmark demonstrating that。)对于您的示例,通用arround-adivce将如下所示:
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在运行时尝试进行转换,使这种通用拦截成为可能。