Implementing guidelines for MethodDelegation

I really liked the description of the method described here:

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

This works well:

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

      

I can intercept the calls and grab the arguments passed to the methods, which is half of what I want to achieve. However, I haven't found an equally concise way to capture the return value. I know I can get the Callable passed to the interceptor that makes the call, but going down that road seems like a sure way to mess up my stacks.

I think there should be a simple and canonical way to implement the "around-method" pattern.

Before I start digging into the API for reals: am I missing something?

+3
java bytecode byte-buddy


source to share


1 answer


No, you are missing nothing.

Whenever you manipulate code with Byte Buddy, this manipulation will be reflected in your application's stack trace. This is intentional as it makes debugging much easier if something goes wrong. Think about your log interceptor throwing an exception at runtime; if the intercept has somehow merged with your original method, it would be quite difficult for other developers to understand. With the Byte Buddy approach, you can simply go to the calling source, since your interceptor is actually available from there. With Byte Buddy, an exception is never thrown from the generated code, so any problem can be traced back to the source code.

Also, merging stack frames can have strange side effects for code-sensitive code. For example, a security manager might grant higher permissions to the interceptor than to the intercepted code. Merging stack frames will override these permissions.

Writing an injected interceptor @Super Callable

is the canonical way to implement arround-advice. Don't worry about performance. Byte Buddy is written in such a way that the JIT compiler greatly simplifies inline code, so a super-method call is most likely done with zero overhead. (There is even a benchmark that demonstrates this .) For your example, a generic arround-adivce attribute would look like this:



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

      

For each method, the time it takes to execute is now printed to the console. You are delegating this code with MethodDelegation.to(TimingInterceptor.class)

.

Make sure you are using annotation @RuntimeType

. So Byte Buddy tries to cast at runtime, which makes this common interception possible.

+2


source to share







All Articles