Interceptando chamadas para expressões lambda do Java 8 usando Byte Buddy

Eu tento interceptar chamadas para methods e chamadas para expressões lambda Java 8 usando um Byte Buddy AgentBuilder seguinte maneira:

 static { final Instrumentation inst = ByteBuddyAgent.install(); new AgentBuilder.Default() .type(ElementMatchers.nameContainsIgnoreCase("foo")) .transform((builder, typeDescription) -> builder.method(ElementMatchers.any()) .intercept(MethodDelegation.to(LogInterceptor.class))) .installOn(inst); } public static class LogInterceptor { @RuntimeType public static Object log(@SuperCall Callable superCall) throws Exception { System.out.println("yeah..."); return superCall.call(); } } 

Estou usando o Byte Buddy v0.7.1.

Pode interceptar a seguinte Runnable (class anônima):

 FunnyFramework.callMeLater(new Runnable() { @Override public void run() { System.out.println("Hello from inner class"); } }); 

e, claro, todas as chamadas para objects definidos como classs normais (não anônimas). Mas a interceptação não funciona para a expressão lambda:

 FunnyFramework.callMeLater(() -> { System.out.println("Hello from lambda"); }); 

Como posso interceptar também as chamadas de expressão lambda? Não existe um LambdaInterceptor em Byte Buddy, até onde eu sei.

A máquina virtual Java não permite a transformação de arquivos de class que representem uma expressão lambda. Classes que representam expressões lambda são carregadas pelos chamados carregadores de classs anônimos (não confundir com classs anônimas clássicas) que herdam o contexto de segurança de outra class, por exemplo, uma class carregada com um carregador de classs anônimo que liga a class carregada a outra class Foo pode acessar methods private de Foo . Esse carregamento acontece explicitamente usando a API sun.misc.Unsafe .

Byte Buddy conecta-se à API de instrumentação Java, que permite que a aplicação de ClassFileTransformer se conecte ao processo de carregamento de um ClassLoader . Como os carregadores de classs anônimos não são considerados ClassLoader no senso comum, a API de instrumentação não permite tais instrumentações e, portanto, proíbe a instrumentação de expressões lambda.

Claro que isso é lamentável para alguns casos de uso, mas na maioria dos aplicativos da vida real, não há nenhum requisito real para instrumentar a expressão lambda. Muitas instrumentações do mundo real são, por exemplo, aplicadas a methods que são anotados com uma determinada anotação, o que não é possível aplicar a expressões lambda ou a classs que são mais complexas do que uma interface funcional.


ATUALIZAÇÃO : Com o Byte Buddy versão 1.1.0, é possível instrumentar classs que representem expressões lambda. Para isso, o Byte Buddy instrumenta o LambdaMetafactory da JVM e substitui a geração da class por uma definição customizada. Para ativar esse recurso, execute a seguinte etapa no construtor:

 new AgentBuilder.Default() .with(LambdaInstrumentationStrategy.ENABLED) 

Observe que isso funciona apenas com o OpenJDK 8u40; nas versões anteriores, há um bug relacionado a sites de chamadas invokedynamic que impede que isso funcione.

    Intereting Posts