2017-01-16 2 views
2

Ich schrieb dieses Beispiel:Wie können wir auf Methoden zugreifen, die von ByteBuddy zur Kompilierungszeit generiert wurden?

E someCreateMethod(Class<E> clazz) { 
    Class<? extends E> dynamicType = new ByteBuddy() 
      .subclass(clazz) 
      .name("NewEntity") 
      .method(named("getNumber")) 
      .intercept(FixedValue.value(100)) 
      .defineField("stringVal", String.class, Visibility.PRIVATE) 
      .defineMethod("getStringVal", String.class, Visibility.PUBLIC) 
      .intercept(FieldAccessor.ofBeanProperty()) 
      .make() 
      .load(clazz.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
      .getLoaded(); 

    return dynamicType.newInstance(); 
} 

Und ich möchte es verwenden, um die neu definierten number atributte zu erhalten:

Integer num = someCreateMethod(EntityExample.class).getNumber(); //(1) 

Oder das neu definierte stringVal Attribut zu erhalten:

String sVal = someCreateMethod(EntityExample.class).getStringVal(); //(2) 

Mein Problem ist, dass (1) ganz gut funktioniert, während (2) nicht funktioniert. Ich erhalte den folgenden Fehler:

Error:(40, 67) java: cannot find symbol 

symbol: method getStringVal() 

Auch ist es möglich, so etwas wie dies mit einer dynamisch generierte Klasse zu tun:

NewEntity newEntity = someCreateMethod(EntityExample.class); 
Integer num = newEntity.getNumber(); 
String sVal = newEntity.getStringVal(); 

?

EDIT: Ich schätze Ihre Hilfe, dieses Beispiel war mein erster Versuch mit der ByteBuddy-Bibliothek. Ich dachte, dass defineMethod tatsächlich eine Implementierung einer Interface-Methode definiert, nicht nur eine zufällige Methode zu der Klasse hinzufügen. Also habe ich beschlossen, hier zu erklären, was genau ich erreichen möchte.

Für jedes Date Attribut in einer Klasse E, möchte ich zwei weitere Felder hinzuzufügen (und ihre Entsprechungen Getter und Setter), die (atribute name)InitialDate und (atribute name)FinalDate, sagen lassen, so dass ich Intervalle functinality für jedes Datum in E verwenden können.

Ich fragte mich, ob ich Code-Generierung verwenden könnte, um diese Methoden hinzuzufügen, ohne für jede E Subklassen erstellen zu müssen.

PS: E kann nicht geändert werden, es gehört zu einem Legacy-Modul.

PS2: Ich weiß nicht, wie viel Datum Attribute gibt es in jedem Unternehmen sein würde E, aber das neue attibutes und Methoden würden Konventionen erstellt werden (zB __FisrtDay, __LastDay), wie unten gezeigt:

NewA a = eb.create(A.class); 
a.getDeadLine(); //inherited 
a.getDeadLineFirstDay(); //added 
a.getDeadLineLastDay(); //added 

NewA b = eb.create(B.class); 
b.getBirthday(); //inherited 
b.getBirthdayFirstDay(); //added 
b.getBirthdayLastDay(); //added 

b.getAnniversary(); //inherited 
b.getAnniversaryFirstDay(); //added 
b.getAnniversaryLastDay(); //added 

PS3: Ist das, was ich versuche, mit ByteBuddy oder überhaupt überhaupt möglich? Gibt es eine andere Art und Weise?

PS4: Sollte mein EDIT eine neue Frage gewesen sein?

+0

Ich gehe davon aus, dass 'getNumber' in der' EntityExample' Schnittstelle definiert ist, wo 'getStringVal' nicht ist. –

+0

Verzeihen Sie auch die dumme Frage meinerseits ... aber warum denken Sie, dass Sie das tun wollen? –

+0

@Joe C Nun, ByteBuddy lässt uns neue Methoden definieren, oder? (seit dem ".defineMethod" -Teil) Ich denke, es sollte uns einen Weg geben, auf die Methoden zuzugreifen, die wir erschaffen ... Ich weiß einfach nicht, wie ich es machen soll. Wie für Ihren zweiten Kommentar muss ich dynamisch Unterklassen jeder Klasse erstellen, die ich als Parameter an die Methode "someCreateMethod" übergeben, und diese Unterklassen sollten zwei neue Methoden implementieren. Deshalb will ich das. –

Antwort

4

Sie benötigen E eine übergeordnete Klasse/oder Schnittstelle zu sein, die die Methoden enthält, die Sie anrufen möchten - Sie werden nicht in der Lage sein subtyped Methoden zu lösen, die auf E.

existieren nicht

Dies ist kein ByteBuddy Problem, das ist ein Problem Ihres Klassenentwurfs - Sie sollten & die zu generierende Funktionalität in abstrahierbare Teile gruppieren, sodass sie über Typen verfügbar gemacht werden können, die zur Kompilierungszeit bedeutsam sind.

Zum Beispiel könnten wir einen Supertyp 'ValueProvider' verwenden und dann mit ByteBuddy einen IntConstantProvider definieren.

public interface ValueProvider<T> { 
    public T getValue(); 
} 

Class<? extends ValueProvider<Integer>> dynamicType = new ByteBuddy() 
    .subclass(clazz) 
    .name("ConstantIntProvider") 
    .method(named("getValue")) 
    .intercept(FixedValue.value(100)) 
    // etc. 

Ihr Prototyp hatte 3 separate Funktionalitäten ohne offensichtliche Abstraktion umfassen sie (wenn wir Nichtbezug private Felder zu der Stub einiger beabsichtigten Verhalten berücksichtigen). Dies könnte besser als 3 einfache atomare Verhaltensweisen ausgelegt werden, für die die Abstraktionen offensichtlich wären.

Sie könnten reflection verwenden, um beliebige Methoden für eine beliebige dynamisch definierte Klasse zu finden, aber das ist nicht wirklich sinnvoll von einem Codierungs- oder Design-POV (wie weiß Ihr Code, welche Methoden aufgerufen werden sollen?) Verwenden Sie einen Typ, um das auszudrücken?) noch ist es sehr performant.

FOLGEN BEARBEITEN ZU FRAGE - Java Bean Eigenschaften arbeiten durch Reflexion, so das Beispiel der Suche nach "verwandten Eigenschaften" (wie First/Last Date) aus bekannten Eigenschaften ist nicht unangemessen.

Es könnte jedoch in Betracht gezogen werden, eine DateInterval (FirstDate, LastDate) -Klasse zu verwenden, so dass nur eine zusätzliche Eigenschaft Per-Base-Eigenschaft benötigt wird.

+0

Danke Thomas! Kannst du mir mal auf die Frage nach EDIT schauen? Ich habe versucht, besser zu erklären, was ich zu tun versuche. Irgendwelche Ideen/Vorschläge/Korrekturen sind willkommen. –

+0

Danke für die Klärung Ihres Zwecks @ReyaGistrout, habe meine Antwort bearbeitet. –

0

Wie Thomas hervorhebt, generiert Byte Buddy zur Laufzeit Klassen, so dass Ihr Compiler ihre Existenz während der Kompilierzeit nicht überprüfen kann.

Sie können Ihre Codegenerierung zur Build-Zeit anwenden. Wenn Ihr EntityExample.class in einem bestimmten Modul vorhanden ist, können Sie dieses Modul mit dem Byte Buddy Maven- oder Gradle-Plugin erweitern und dann nach der Erweiterung Ihrem Compiler erlauben, ihre Existenz zu bestätigen.

Was können Sie auch Schnittstellen tun wäre, zu definieren, wie

interface StringVal { 
    String getStringVal(); 
} 

die Sie Byte Buddy fragen können in der Unterklasse zu implementieren, die Ihr Compiler ermöglicht die Methode der Existenz zu bestätigen, wenn Sie Ihre Unterklasse als diese Schnittstelle darstellen .

Ansonsten tut Ihr Compiler genau das, was er tun soll: Ihnen zu sagen, dass Sie eine Methode aufrufen, die zu dieser Zeit nicht existiert.

Verwandte Themen