2016-03-19 20 views
0

Wir suchen nach einer Lösung, um den Status einer Client-POJO-Instanz performant zu verfolgen. Was wir erwarten, ist: Jedes Mal, wenn eine Änderung an einem POJO vorgenommen wird, wird dieser Zustand durch Verwendung von Settors hergestellt. Wir haben einen OGNL-basierten Beobachtungs-/Event-Bus erstellt und wenn die Änderung gemacht wird, senden wir den richtigen OgnlChangeEvent an unseren Event-Bus.So verfolgen Sie den Status eines POJO mithilfe der Codegenerierung

Bisher haben wir uns die AspectJ/cglib/Objekt-Graph-Diff-Lösung angesehen, aber alle haben zu viel CPU-Zeit beansprucht. Unsere aktuelle Lösung basiert auf Spring MethodInterceptor und wir erstellen eine neue Proxy Instanz jedes Mal, wenn eine Getter-Methode aufgerufen wird.

An diesem Punkt fangen wir an, Codegenerationslösungen zu betrachten, und wir stießen auf Byte Buddy. Ist diese Richtung der richtige Weg? Können wir einen neuen Class generieren, der unseren POJO-Status des Clients erweitert und über sein OGNL-Präfix benachrichtigt, bis die Setter-Methode aufgerufen wird?

+0

Können Sie das Leistungsproblem mit einigen Messungen näher erläutern? Wie oft rufen Sie eine Methode auf, was ist die Ausführungszeit der Methode ohne Änderungsverfolgung, was ist die Ausführungszeit mit der Änderungsverfolgung und eine Übersicht darüber, was Sie im Änderungsverfolgungsteil Ihres Codes tun? –

+0

Ich stimme mit @ NándorElődFekete überein. Es ist kaum zu glauben, dass AspectJ langsamer sein sollte als Spring AOP. Wenn ja, müssen Sie etwas falsch machen, weil AspectJ sehr effizient ist. Vielleicht könnten wir Ihnen helfen, wenn wir Ihre Aspekte sehen würden. Aber wenn Sie mit ByteBuddy zufrieden sind, ist dieser Kommentar vielleicht veraltet. Ich habe in letzter Zeit nicht viel hier gelesen, weil ich beschäftigt war, also bin ich ein bisschen spät dran, vielleicht hier zu kommentieren. – kriegaex

Antwort

2

Byte Buddy ist ein Werkzeug zur Codegenerierung, und es ist natürlich möglich, eine solche Lösung zu implementieren. Für eine Klasse erstellen, die einen Setter abfängt, würde schreiben Sie Code wie:

new ByteBuddy() 
    .subclass(UserPojo.class) 
    .method(ElementMatchers.isSetter()) 
    .intercept(MethodDelegation.to(MyInterceptor.class) 
      .andThen(SuperMethodCall.INSTANCE) 
    .make(); 

Wo Sie ein Abfangjäger so schreiben würde:

public class MyInterceptor { 
    public static void intercept(Object value) { 
    // business logic comes here 
    } 
} 

Auf diese Weise können Sie einen Code jedes Mal ein Setter hinzufügen wird aufgerufen, die vor dem ursprünglichen Code ausgelöst wird. Sie können die Abfangmethode auch mit allen primitiven Typen überladen, um das Boxen für das Argument zu vermeiden. Byte Buddy findet heraus, was Sie für Sie tun können.

Ich bin jedoch verwirrt was Sie unter performant verstehen. Der obige Code wendet sich an mich das gleiche wie Erstellen einer Klasse wie:

class UserClass { 
    String value; 
    void setValue(String value) { 
    this.value = value; 
    } 
} 

class InstrumentedUserClass extends UserClass { 
    @Override 
    void setValue(String value) { 
    MyInterceptor.intercept(value); 
    super.setValue(value); 
    } 
} 

Die Performance wird in erster Linie durch die Leistung beeinflusst, was Sie in der intercept Methode tun.

Schließlich verstehe ich nicht, wie cglib nicht für Sie funktioniert, aber die Verwendung von Spring - die auf cglib gebaut wird - funktioniert. Ich vermute, dass es ein Problem mit Ihrer Überwachungslogik gibt, die Sie untersuchen sollten.

+0

danke :-) CGLIB funktioniert für mich genauso wie die Federwickel-Lösung. Das Hauptproblem war und ist immer noch die Performance :-( –

1

Ich denke, dass Leistung hängt nicht viel von der Bytecode Instrumentierung Framework Sie verwenden, sondern hängt davon ab, was Sie in einer Methode Interceptor tun. Endlich wissen Sie nur, ob Sie messen.

Ich weiß nicht viel über Ihren Anwendungsfall, aber im Allgemeinen würde ich frage mich:

  • Welche Informationen brauche ich wirklich?
  • Was sind die grundlegenden Daten, um diese Informationen zu erhalten?

Sie sollten grundlegende Datensammlung von der Interpretation dieser Daten (die Informationen) trennen. Normalerweise dauert die Interpretation länger. Grunddaten sind Daten, die nicht aus anderen Daten abgeleitet werden können. ZB Geburtstag ist Basisdaten, während das Alter aus dem Geburtstag stammt.

Bei einem Verfahren Abfangjäger Ich würde

  • sammeln nur die Basisdaten wie Klassennamen, Methodennamen und vielleicht Parameter.
  • senden Sie diese Informationen an eine Art von Worker Queue
  • Lassen Sie einen Hintergrundarbeiter die Informationen generieren, protokollieren oder persit es.

Der Hintergrundarbeiter kann z.B. interpretieren Sie den Methodennamen, um herauszufinden, ob es sich um einen Eigenschaftsaccessor handelt oder nicht. Normalerweise tun Sie dies mit der BeanInfo, die Sie von einer Introspector oder zumindest die Reflexion API erhalten.

+0

Danke René! Der aufwendigste Teil war das Erstellen eines neuen Proxy für jede Getter-Methode, um weiterhin das Ognl-Präfix zu bekommen. Deshalb suchte ich nach Byte-Instrumentierungslösung. –