2015-06-11 10 views
6

Ich habe ein Java6-Projekt, das nach Java8 migriert wird. Wir haben aspectj verwendet, um einige Aktionen des Benutzers zu protokollieren, wie zum Beispiel das Klicken auf die Schaltfläche.Aspectj: Pointcut auf Lambda-Ausdruck

So gibt es Hörern wie folgt aus:

button.addClickListener(new Button.ClickListener() { 
     @Override 
     public void buttonClick(Button.ClickEvent clickEvent) { 
      doSth(); 
     } 
    }); 

Und poincut:

@Pointcut("execution(public void Button.ClickListener.buttonClick(Button.ClickEvent))") 
public void buttonClick() {}; 

Aber da wir Java8 verwenden, werden die Zuhörer so aussehen:

button.addClickListener(clickEvent -> doSth()); 

Gibt es eine Möglichkeit, aspectj pointcut zu schreiben, so dass es neue Listener behandelt?

+0

Ich denke, Ihr 'pointcut' sollte immer noch funktionieren, weil Sie den' addClickListener' so geändert haben, dass 'Lambda' verwendet wird und Ihr' Button.ClickListener.buttonClick' immer noch den gleichen Namen hat. Hast du das auch geändert? –

+0

Es funktioniert definitiv nicht. Ich habe AddClickListener nur geändert, um Lambda zu verwenden. Ja, es verwendet immer noch Button.ClickListener.buttonClick, aber die Funktion, die vor Pointcut ausgeführt werden soll (markiert als: @Before ("buttonClick()") wird nie aufgerufen. Es wird aufgerufen, wenn Nicht-Lambda-Definition verwendet wird. – Lete

+0

Das bedeutet also dass AspectJ nicht in der Lage ist, die Methoden der durch die 'LambdaMetaFactory' erzeugten Listener-Klasse zur Laufzeit zu patchen. Kann es aber' default' Methoden von 'Schnittstellen' modifizieren? Das würde zu einer möglichen Lösung führen ... – Holger

Antwort

2

Ich vermute das Problem ist, dass Lambdas nicht tatsächlich implementieren/überschreiben alle Schnittstellenmethoden mit einem entsprechenden Namen, sondern erstellen Sie eine anonyme Methode. Schauen Sie sich dieses Beispiel:

Dummy Schaltfläche Klasse, die Replikation, die Teile von Vaadin wir brauchen hier:

package de.scrum_master.app; 

public class Button { 
    private ClickListener listener; 

    public void addClickListener(ClickListener listener) { 
     this.listener = listener; 
    } 

    public void click() { 
     System.out.println("Clicking button"); 
     listener.buttonClick(new ClickEvent()); 
    } 

    public static class ClickEvent {} 

    public static interface ClickListener { 
     public void buttonClick(ClickEvent clickEvent); 
    } 
} 

Treiber Anwendung:

package de.scrum_master.app; 

public class Application { 
    protected static void doSomething() {} 

    public static void main(String[] args) { 
     Button button = new Button(); 
     button.addClickListener(new Button.ClickListener() { 
      @Override 
      public void buttonClick(Button.ClickEvent clickEvent) { 
       doSomething(); 
      } 
     }); 
     button.click(); 

     button = new Button(); 
     button.addClickListener(clickEvent -> doSomething()); 
     button.click(); 
    } 
} 

Wie Sie sehen können, mit zwei Tasten Instanzen werden erstellt, eine mit einem klassischen anonymen Klassen-Listener, eine mit einem Lambda-Listener. Beide Tasten geklickt erhalten, so damit das Konsolenprotokoll sieht wie folgt aus:

Clicking button 
Clicking button 

Richtung:

package de.scrum_master.aspect; 

import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 

@Aspect 
public class ButtonClickLogger { 
    @Before("execution(public void *..Button.ClickListener.buttonClick(*..Button.ClickEvent))") 
    public void logButtonClick(JoinPoint thisJoinPoint) { 
     System.out.println("Caught button click: " + thisJoinPoint); 
    } 

    @Before("within(*..Application)") 
    public void logAllInListener(JoinPoint thisJoinPoint) { 
     System.out.println(" " + thisJoinPoint); 
    } 

    @Before("execution(void *..lambda*(*..Button.ClickEvent))") 
    public void logButtonClickLambda(JoinPoint thisJoinPoint) { 
     System.out.println("Caught button click (lambda): " + thisJoinPoint); 
    } 
} 

Die erste Beratung verwendet eine pointcut ähnlich wie bei Ihnen. Es kann nur klassische Listener-Deklarationen abfangen.

Der zweite Tipp ist für Debugging-Zwecke und protokolliert alle Joinpoints innerhalb der Treiber-Anwendung, um zu zeigen, was zum Teufel hier vorgeht. Der letzte Hinweis zeigt eine Problemumgehung zum Abfangen von lambda-basierten Listenern, wobei man sich auf das Wissen über die Java-Compiler-Benennung für Lambda verlässt, die von der Debug-Ausgabe erfasst wurden. Das ist nicht sehr nett, aber im Moment funktioniert es. Bitte beachten Sie, dass die Lambda-Version von buttonClick(..)nicht öffentlich ist, so ist es nur void *..lambda*, nicht public void *..lambda*.

Konsolenausgabe mit AspectJ:

staticinitialization(de.scrum_master.app.Application.<clinit>) 
    execution(void de.scrum_master.app.Application.main(String[])) 
    call(de.scrum_master.app.Button()) 
    call(de.scrum_master.app.Application.1()) 
    staticinitialization(de.scrum_master.app.Application.1.<clinit>) 
    preinitialization(de.scrum_master.app.Application.1()) 
    initialization(de.scrum_master.app.Application.1()) 
    initialization(de.scrum_master.app.Button.ClickListener()) 
    execution(de.scrum_master.app.Application.1()) 
    call(void de.scrum_master.app.Button.addClickListener(Button.ClickListener)) 
    call(void de.scrum_master.app.Button.click()) 
Clicking button 
Caught button click: execution(void de.scrum_master.app.Application.1.buttonClick(Button.ClickEvent)) 
    execution(void de.scrum_master.app.Application.1.buttonClick(Button.ClickEvent)) 
    call(void de.scrum_master.app.Application.doSomething()) 
    execution(void de.scrum_master.app.Application.doSomething()) 
    call(de.scrum_master.app.Button()) 
    call(void de.scrum_master.app.Button.addClickListener(Button.ClickListener)) 
    call(void de.scrum_master.app.Button.click()) 
Clicking button 
    execution(void de.scrum_master.app.Application.lambda$0(Button.ClickEvent)) 
Caught button click (lambda): execution(void de.scrum_master.app.Application.lambda$0(Button.ClickEvent)) 
    call(void de.scrum_master.app.Application.doSomething()) 
    execution(void de.scrum_master.app.Application.doSomething()) 

Update: Es gibt einen entsprechenden Bugzilla issue jetzt AspectJ. Ich habe es gerade erstellt. Es verweist auch auf eine aktuelle Diskussion auf der Mailing-Liste.

+0

Bitte beachten Sie mein Update mit der Bugzilla-Link – kriegaex

+0

Falls jemand anderes auf ein ähnliches Problem stößt, erzeugen einige lambdas auch statische Methoden, die diese Antwort derzeit nicht verfolgt –

+0

Nun, es gab keine Frage über statische Methoden in lambdas.Wären Sie etwas dagegen, genauer zu sein und Beispielcode zur Verfügung stellen? Sie könnten eine neue Frage erstellen, die Ihr Problem mit Beispielcode beschreibt, und von hier aus verlinken. Ich würde gerne nach Ihnen suchen und helfen, wenn Ich kann. – kriegaex

Verwandte Themen