2015-11-25 34 views
5

cerco di intercettare le chiamate ai metodi e le chiamate a Java 8 espressioni lambda utilizzando un byte compagno AgentBuilder come segue:intercettare le chiamate a Java 8 lambda-espressioni utilizzando Byte amici

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

sto usando Byte amici v0 .7.1.

Può intercettare il seguente Runnable (classe anonima):

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

e naturalmente eventuali chiamate agli oggetti definiti come normali classi (non anonimi). Ma l'intercettazione non funziona per un'espressione lambda:

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

Come posso intercettare anche le chiamate di espressione lambda? Non esiste una cosa come la LambdaInterceptor in Byte Buddy, per quanto ne so.

+2

La strumentazione delle espressioni lambda è ora possibile a partire da Byte Buddy 1.1.0 –

risposta

7

La macchina virtuale Java non consente la trasformazione di file di classe che rappresentano un'espressione lambda. Le classi che rappresentano le espressioni lambda vengono caricate dal cosiddetto anonymous class loaders (da non confondere con il classico anonymous classes) che eredita il contesto di sicurezza di un'altra classe, ad es. una classe caricata con un programma di caricamento classi anonimo che associa la classe caricata ad un'altra classe Foo può accedere ai metodi private di Foo. Questo caricamento avviene esplicitamente utilizzando l'API sun.misc.Unsafe.

Byte compagno aggancia nella Java instrumentation API che consente all'applicazione di ClassFileTransformer s agganciare in un ClassLoader s processo di caricamento. Come caricatori di classi anonime non sono considerati ClassLoader s nel senso comune, la strumentazione API non consente tali strumenti e pertanto vieta la strumentazione delle espressioni lambda.

Questo è ovviamente sfortunato per alcuni casi d'uso, ma nella maggior parte delle applicazioni della vita reale, non vi è alcun requisito reale per lo strumento di espressione lambda. Molte strumentazioni del mondo reale sono ad esempio applicate ai metodi annotati con una data annotazione che non è possibile applicare alle espressioni lambda o alle classi che sono più complesse di un'interfaccia funzionale.


UPDATE: Con Byte compagno versione 1.1.0, è possibile classi di strumenti che rappresentano le espressioni lambda. Per questo, Byte Buddy esegue gli strumenti JVM LambdaMetafactory e sostituisce la generazione di classi con una definizione personalizzata. Per attivare questa funzione, eseguire il seguente passo del costruttore:

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

Si noti che questo funziona solo con OpenJDK 8u40, nelle versioni precedenti, c'è un bug relativo ai siti di chiamata invokedynamic che impedisce questo di lavorare.