Byte Buddy ist eine Codegenerierung und Manipulationsbibliothek für die Erstellung und Änderung von Java -Klassen während der Laufzeit einer Java -Anwendung und ohne Hilfe eines Compilers. Anders als die Versorgungsunternehmen der Codegenerierung, die mit der Java -Klassenbibliothek versandt werden, ermöglicht Byte Buddy die Erstellung willkürlicher Klassen und ist nicht auf die Implementierung von Schnittstellen für die Erstellung von Laufzeit -Proxys beschränkt. Darüber hinaus bietet Byte Buddy eine bequeme API für die manuelle Änderung des Klassens, indem Sie einen Java -Agenten oder während eines Builds manuell ändern.
Um Byte Buddy zu verwenden, benötigt man kein Verständnis für den Java -Byte -Code oder das Klassendateiformat. Im Gegensatz dazu zielt die API von Byte Buddy auf Code ab, der für jeden präzise und leicht zu verstehen ist. Byte Buddy bleibt jedoch auf die Möglichkeit, benutzerdefinierten Byte -Code zu definieren, vollständig anpassbar. Darüber hinaus wurde die API so konzipiert, dass sie so nicht störend wie möglich ist, und infolgedessen hinterlässt Byte Buddy keine Spuren in den von ihm erstellten Klassen. Aus diesem Grund können die erzeugten Klassen existieren, ohne dass Byte Buddy auf dem Klassenpfad erforderlich ist. Aufgrund dieses Merkmals wurde Byte Buddys Maskottchen als Geist ausgewählt.
Byte Buddy ist in Java 5 geschrieben, unterstützt aber die Generation von Klassen für jede Java -Version. Byte Buddy ist eine leichte Bibliothek und hängt nur von der Besucher-API der Java-Byte-Code-Parser-Bibliothek ASM ab, für die selbst keine weiteren Abhängigkeiten erforderlich sind.
Auf den ersten Blick kann die Erzeugung von Laufzeitcode eine Art schwarze Magie zu sein, die vermieden werden sollte, und nur wenige Entwickler schreiben Anwendungen, die während ihrer Laufzeit explizit Code generieren. Dieses Bild ändert sich jedoch beim Erstellen von Bibliotheken, die mit willkürlichen Code und Typen interagieren müssen, die zur Kompilierungszeit unbekannt sind. In diesem Zusammenhang muss ein Bibliotheksimplementierer häufig wählen, ob ein Benutzer Bibliotheksbeschwerden implementieren muss, oder ob die Bibliothekstypen Code für die Laufzeit generieren, um Code zu generieren. Viele bekannte Bibliotheken, wie zum Beispiel Spring oder Hibernate, wählen den letzteren Ansatz aus, der unter den Benutzern unter dem Begriff der Verwendung einfacher alter Java -Objekte beliebt ist. Infolgedessen ist die Codegenerierung zu einem allgegenwärtigen Konzept im Java -Raum geworden. Byte Buddy ist ein Versuch, die Laufzeit -Schaffung von Java -Typen zu innovieren, um ein besseres Toolssatz für diejenigen zu bieten, die sich auf die Codegenerierung verlassen.
Im Oktober 2015 wurde Byte Buddy von Oracle mit einem Duke's Choice Award ausgezeichnet. Die Auszeichnung schätzt Byte Buddy für seine " enorme Menge an Innovation in der Java -Technologie ". Wir fühlen uns sehr geehrt, dass wir diese Auszeichnung erhalten haben, und möchten uns bei allen Benutzern und allen anderen bedanken, die Byte Buddy zu dem Erfolg haben, den sie geworden ist. Wir schätzen es wirklich!
Byte Buddy bietet eine hervorragende Leistung bei der Produktionsqualität. Es ist stabil und wird durch angesehene Frameworks und Tools wie Mockito, Hibernate, Jackson, Googles Bazel Build -System und viele andere verwendet. Byte Buddy wird auch von einer großen Anzahl von kommerziellen Produkten verwendet, um ein großes Ergebnis zu erzielen. Es wird derzeit über 75 Millionen Mal im Jahr heruntergeladen.
Hello World mit Byte Buddy zu sagen ist so einfach wie möglich. Jede Erstellung einer Java -Klasse beginnt mit einer Instanz der ByteBuddy
-Klasse, die eine Konfiguration zum Erstellen neuer Typen darstellt:
Class <?> dynamicType = new ByteBuddy ()
. subclass ( Object . class )
. method ( ElementMatchers . named ( "toString" ))
. intercept ( FixedValue . value ( "Hello World!" ))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat ( dynamicType . newInstance (). toString (), is ( "Hello World!" ));
Die Standard ByteBuddy
-Konfiguration, die im obigen Beispiel verwendet wird, erstellt eine Java -Klasse in der neuesten Version des Klassendateiformates, das von der Virtual Machine verarbeitungsverarbeitung verstanden wird. Aus dem Beispielcode hoffentlich offensichtlich, erweitert der erstellte Typ die Object
und überschreibt seine toString
-Methode, die einen festen Wert von Hello World!
. Die zu überschreibende Methode wird durch einen sogenannten ElementMatcher
identifiziert. Im obigen Beispiel wird ein vordefinierter Element -Matcher named(String)
verwendet, der Methoden nach ihren genauen Namen identifiziert. Byte Buddy ist mit zahlreichen vordefinierten und gut getesteten Matchern geliefert, die in der ElementMatchers
-Klasse gesammelt werden und die leicht komponiert werden können. Die Erstellung benutzerdefinierter Matcher ist jedoch so einfach wie die Implementierung der (funktionalen) ElementMatcher
-Schnittstelle.
Für die Implementierung der toString
-Methode definiert die FixedValue
-Klasse einen konstanten Rückgabewert für die überschriebene Methode. Das Definieren eines konstanten Wertes ist nur ein Beispiel für viele Methodenabfangwäsche, die mit Byte -Kumpel versandt werden. Durch die Implementierung der Implementation
könnte eine Methode jedoch sogar durch benutzerdefinierten Byte -Code definiert werden.
Schließlich wird die beschriebene Java -Klasse erstellt und dann in die virtuelle Java -Maschine geladen. Zu diesem Zweck ist ein Zielklassenlader erforderlich. Schließlich können wir uns von dem Ergebnis überzeugen, indem wir die toString
-Methode in einer Instanz der erstellten Klasse aufrufen und den Rückgabewert finden, um den von uns erwarteten konstanten Wert darzustellen.
Natürlich ist ein Hello World -Beispiel ein zu einfacher Anwendungsfall für die Bewertung der Qualität einer Codegenerierungsbibliothek. In Wirklichkeit möchte ein Benutzer einer solchen Bibliothek komplexere Manipulationen ausführen, beispielsweise durch Einführung von Haken in den Ausführungspfad eines Java -Programms. Die Verwendung von Byte Buddy ist jedoch ebenso einfach. Das folgende Beispiel gibt einen Vorgeschmack darauf, wie Methodenaufrufe abgefangen werden können.
Byte Buddy drückt dynamisch definierte Methodenimplementierungen nach Instanzen der Implementation
aus. Im vorherigen Beispiel wurde bereits festgestellt, FixedValue
, der diese Schnittstelle implementiert, bereits demonstriert. Durch die Implementierung dieser Schnittstelle kann ein Benutzer von Byte Buddy die Länge des Definierens benutzerdefinierter Byte -Code für eine Methode überschreiten. Normalerweise ist es jedoch einfacher, vordefinierte Implementierungen von Byte Buddy wie MethodDelegation
zu verwenden, die die Implementierung einer Methode in einfacher Java ermöglichen. Die Verwendung dieser Implementierung ist einfach, da sie arbeitet, indem der Steuerfluss an jedes Pojo delegiert. Als Beispiel für ein solches Pojo kann Byte Buddy beispielsweise einen Aufruf zur einzigen Methode der folgenden Klasse umleiten:
public class GreetingInterceptor {
public Object greet ( Object argument ) {
return "Hello from " + argument ;
}
}
Beachten Sie, dass der obige GreetingInterceptor
nicht von einem Byte -Buddy -Typ abhängt. Dies sind gute Nachrichten, da keiner der Klassen, die Byte Buddy generiert, Byte Buddy auf dem Klassenpfad benötigt! Angesichts des oben genannten GreetingInterceptor
können wir Byte Buddy verwenden, um die Java 8 java.util.function.Function
-Schnittstelle und ihre Abstract apply
-Methode zu implementieren:
Class <? extends java . util . function . Function > dynamicType = new ByteBuddy ()
. subclass ( java . util . function . Function . class )
. method ( ElementMatchers . named ( "apply" ))
. intercept ( MethodDelegation . to ( new GreetingInterceptor ()))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat (( String ) dynamicType . newInstance (). apply ( "Byte Buddy" ), is ( "Hello from Byte Buddy" ));
Das Ausführen des obigen Codes implementiert Byte Buddy Function
von Java und implementiert die apply
als Delegation einer Instanz des zuvor definierten GreetingInterceptor
-Pojo. Jedes Mal, wenn die Function::apply
aufgerufen wird, wird der Steuerfluss in GreetingInterceptor::greet
versandt und der Rückgabewert der letzteren Methode wird von der Methode der Schnittstelle zurückgegeben.
Interceptors können definiert werden, um generische Eingänge und Ausgänge zu erhalten, indem die Parameter des Interceptor -Parameters annotiert. Wenn Byte Buddy eine Annotation entdeckt, injiziert die Bibliothek die Abhängigkeit, die der Interceptor -Parameter benötigt. Ein Beispiel für einen allgemeineren Interceptor ist die folgende Klasse:
public class GeneralInterceptor {
@ RuntimeType
public Object intercept ( @ AllArguments Object [] allArguments ,
@ Origin Method method ) {
// intercept any method of any signature
}
}
Mit dem obigen Interceptor könnte jede abgefangene Methode übereinstimmen und verarbeitet werden. Wenn beispielsweise die Übereinstimmung Function::apply
, werden die Argumente der Methode als einzelnes Element eines Arrays übergeben. Außerdem würde ein Method
auf Fuction::apply
als zweites Argument des Interceptors aufgrund der @Origin
-Annotation übergeben. Byte Buddy erklärt den zurückgegebenen Wert auf den Rückgabewert der abgefangenen Methode, @RuntimeType
dies erforderlich ist. Byte Buddy wendet auch automatische Boxen und Unboxen an.
Neben den bereits erwähnten Anmerkungen gibt es viele andere vordefinierte Anmerkungen. Wenn beispielsweise die @SuperCall
-Annotation an einem Runnable
oder Callable
Typ verwendet wird, wird Byte Buddy Proxy-Instanzen injiziert, die eine Aufruf einer nicht abstrakten Supermethode ermöglichen, wenn eine solche Methode vorliegt. Und selbst wenn Byte Buddy keinen Anwendungsfall abdeckt, bietet Byte Buddy einen Erweiterungsmechanismus zur Definition benutzerdefinierter Anmerkungen.
Sie können erwarten, dass die Verwendung dieser Anmerkungen Ihren Code an Byte Buddy verbindet. Java ignoriert jedoch Anmerkungen, falls sie für einen Klassenlader nicht sichtbar sind. Auf diese Weise kann generierter Code noch ohne Byte -Kumpel existieren! Weitere Informationen zur MethodDelegation
und zu all ihren vordefinierten Anmerkungen in seinem Javadoc und in Byte Buddys Tutorial finden Sie.
Byte Buddy ist nicht darauf beschränkt, Unterklassen zu erstellen, sondern auch den vorhandenen Code neu definieren. Byte Buddy bietet dazu eine bequeme API für die Definition sogenannter Java-Agenten. Java -Agenten sind einfache alte Java -Programme, mit denen der Code einer vorhandenen Java -Anwendung während seiner Laufzeit geändert werden kann. Als Beispiel können wir Byte Buddy verwenden, um die Methoden zu ändern, um ihre Ausführungszeit zu drucken. Dafür definieren wir zunächst einen Interceptor, der den Interceptors in den vorherigen Beispielen ähnlich ist:
public class TimingInterceptor {
@ RuntimeType
public static Object intercept ( @ Origin Method method ,
@ SuperCall Callable <?> callable ) {
long start = System . currentTimeMillis ();
try {
return callable . call ();
} finally {
System . out . println ( method + " took " + ( System . currentTimeMillis () - start ));
}
}
}
Mit einem Java -Agenten können wir diesen Interceptor jetzt auf alle Typen anwenden, die einem ElementMatcher
für eine TypeDescription
übereinstimmen. Zum Beispiel entscheiden wir uns dafür, alle Typen mit einem Namen, der Timed
endet, den obigen Interceptor hinzuzufügen. Dies geschieht im Einfachheit halber, während eine Annotation wahrscheinlich eine angemessenere Alternative wäre, um solche Klassen für einen Produktionsagenten zu markieren. Mit AgentBuilder
-API von Byte Buddy ist das Erstellen eines Java -Agenten so einfach wie die folgende Agentenklasse zu definieren:
public class TimerAgent {
public static void premain ( String arguments ,
Instrumentation instrumentation ) {
new AgentBuilder . Default ()
. type ( ElementMatchers . nameEndsWith ( "Timed" ))
. transform (( builder , type , classLoader , module , protectionDomain ) ->
builder . method ( ElementMatchers . any ())
. intercept ( MethodDelegation . to ( TimingInterceptor . class ))
). installOn ( instrumentation );
}
}
Ähnlich wie bei der main
von Java ist die premain
-Methode der Einstiegspunkt für jeden Java -Agenten, aus dem wir die Neudefinition anwenden. Als ein Argument erhält ein Java -Agent eine Instanz der Instrumentation
, mit der Byte Buddy die Standard -API der JVM für die Rennzeitklasse -Neudefinition einbinden kann.
Dieses Programm ist zusammen mit einer Manifestdatei mit dem Attribut Premain-Class
auf den TimerAgent
gepackt. Die resultierende JAR -Datei kann jetzt zu jeder Java -Anwendung hinzugefügt werden, indem -javaagent:timingagent.jar
ähnlich dem Hinzufügen eines Glass zum Klassenpfad eingestellt werden. Wenn der Agent aktiv ist, drucken alle im Timed
Klassen jetzt ihre Ausführungszeit in die Konsole.
Byte Buddy ist auch in der Lage, sogenannte Laufzeitanhänge anzuwenden, indem Änderungen der Klassendatei-Format und der Advice
verwendet werden. Weitere Informationen finden Sie im Javadoc des Advice
und der AgentBuilder
-Klasse. Byte Buddy bietet auch die explizite Änderung der Java -Klassen über eine ByteBuddy
-Instanz oder durch Verwendung der Byte Buddy Maven- und Gradle -Plugins an.
Byte Buddy ist eine umfassende Bibliothek und wir haben nur die Oberfläche der Fähigkeiten von Byte Buddy kratzt. Byte Buddy zielt jedoch darauf ab, durch eine domänenspezifische Sprache für das Erstellen von Klassen einfach zu bedienen zu können. Die meisten Laufzeit -Codegenerierung können durch das Schreiben von lesbarem Code und ohne Wissen über das Klassendateiformat von Java erfolgen. Wenn Sie mehr über Byte Buddy erfahren möchten, finden Sie ein solches Tutorial auf der Webseite von Byte Buddy (es gibt auch eine chinesische Übersetzung).
Darüber hinaus verfügt Byte Buddy mit einer detaillierten In-Code-Dokumentation und einer umfangreichen Testfallabdeckung, die auch als Beispielcode dienen kann. Schließlich finden Sie im Wiki eine aktuelle Liste von Artikeln und Präsentationen auf Byte Buddy. Lesen Sie bei der Verwendung von Byte Buddy auch die folgenden Informationen zur Aufrechterhaltung einer Projektabhängigkeit.
Die Verwendung von Byte Buddy ist kostenlos und erfordert nicht den Kauf einer Lizenz. Um das Beste aus der Bibliothek herauszuholen oder einen einfachen Start zu sichern, ist es jedoch möglich, Schulungen, Entwicklungszeiten oder Unterstützungspläne zu kaufen. Die Raten hängen vom Umfang und der Dauer eines Engagements ab. Bitte setzen Sie sich mit [email protected] in Verbindung, um weitere Informationen zu erhalten.
Byte Buddy ist auf Tidelift aufgeführt. Wenn Sie Byte Buddy nicht in einem Ausmaß verwenden, in dem Sie explizite Unterstützung erwerben und die Open -Source -Community im Allgemeinen unterstützen möchten, sollten Sie bitte ein Abonnement in Betracht ziehen.
Sie können meine Arbeit über Github -Sponsoren unterstützen. Beachten Sie, dass diese Option nur für kommerzielle Akteure gedacht ist, die nach einem einfachen Zahlungskanal suchen und keine Unterstützung im Gegenzug erwarten. Die Unterstützung über Github -Sponsoren ist nicht möglich, um die Einhaltung der Mehrwertsteuer aufrechtzuerhalten. Bitte wenden Sie sich stattdessen nach einer direkten Unterstützungsvereinbarung.
Allgemeine Fragen können auf Stack Overflow oder auf der Byte Buddy Mailing -Liste gestellt werden, die auch als Archiv für Fragen dienen. Natürlich werden Fehlerberichte auch außerhalb eines kommerziellen Plans berücksichtigt. Bei Open -Source -Projekten ist es manchmal möglich, eine erweiterte Hilfe für die Nutzung von Byte Buddy zu erhalten.
Byte Buddy ist auf ASM geschrieben, einer ausgereiften und gut getesteten Bibliothek zum Lesen und Schreiben kompilierter Java-Kurse. Um fortgeschrittene Manipulationen zu ermöglichen, gibt Byte Buddy die ASM -API seinen Benutzern absichtlich aus. Natürlich bleibt die direkte Verwendung von ASM vollständig optional und die meisten Benutzer werden es höchstwahrscheinlich nie benötigen. Diese Auswahl wurde so getroffen, dass ein Benutzer von Byte Buddy nicht auf seine höhere Funktionalität zurückgehalten wird, sondern benutzerdefinierte Implementierungen ohne Aufhebens implementieren kann, wenn dies erforderlich ist.
ASM hat zuvor seine öffentliche API geändert, fügte jedoch einen Mechanismus für die API -Kompatibilität hinzu, beginnend mit Version 4 der Bibliothek. Um Versionskonflikte mit solchen älteren Versionen zu vermeiden, verpackt Byte Buddy die ASM -Abhängigkeit in seinen eigenen Namespace um. Wenn Sie ASM direkt verwenden möchten, bietet das Artefakt byte-buddy-dep
eine Version von Byte Buddy mit einer expliziten Abhängigkeit von ASM. Wenn Sie dies tun, müssen Sie sowohl Byte Buddy als auch ASM in Ihren Namespace umpacken, um Versionskonflikte zu vermeiden.
Bitte beachten Sie die Sicherheitsrichtlinien dieses Projekts.
Byte Buddy unterstützt die Ausführung in allen JVM -Versionen von Version Five und Long in einem einzigen Glas. Dies geschieht, um die Entwicklung von Java -Agenten zu erleichtern, die häufig ältere oder unbekannte Anwendungen, die nicht aktiv aktualisiert werden, erforderlich sind. Um dies zu ermöglichen und gleichzeitig moderne Java und Funktionen wie CDs oder Klassenvalidierung mit Stack Map-Frames zu unterstützen, werden die Hauptgläser für Byte-Buddy als Mehrfreiseitsglässe, die Klassendateien in Version 5 und acht enthalten. Infolgedessen ist die Glasgröße von Byte Buddy höher, wie man es erwarten würde. Die Größe der JAR -Datei ist normalerweise kein Problem, da die Mehrheit der Klassen von Byte Buddy niemals geladen wird. Die Dateigröße kann jedoch ein Problem sein, wenn Java -Agenten verteilt werden. Da Agenten bereits als einzelnes Glas gebündelt werden müssen, wird daher empfohlen, entweder die grundlegende Java Five-Version oder die Multi-Release-Java-Version der enthaltenen Klassendateien zu entfernen, um dieses Problem zu reduzieren. Dies wird von den meisten Build -Plugins für diesen Zweck unterstützt, z. B. das Maven Shade -Plugin.
Byte Buddy ist unter der liberalen und geschäftsfreundlichen Apache-Lizenz, Version 2.0, lizenziert und ist auf GitHub frei verfügbar. Zusätzlich ist die Byte-Buddy -Verteilung ASM, die unter einer 3-Klausel-BSD-Lizenz veröffentlicht wird.
Byte Buddy -Binärdateien werden an den Repositorys von Maven Central und auf JCenter veröffentlicht. Die Artefaktunterschriften können gegen diesen PGP Public Key validiert werden, der mit Byte Buddy 1.10.3 beginnt. Ältere Versionen können gegen dieses ältere und schwächere Zertifikat validiert werden.
Das Projekt wird mit Maven gebaut. Aus Ihrer Hülle würde das Klonen und Erstellen des Projekts ungefähr so gehen:
git clone https://github.com/raphw/byte-buddy.git
cd byte-buddy
mvn package
Bei diesen Befehlen wird Byte Buddy aus Github geklont und auf Ihrer Maschine gebaut. Weitere Build -Optionen sind in der Root POM -Datei aufgeführt. Byte Buddy kann mit jedem JDK von mindestens Version 6 erstellt werden. Es wird jedoch empfohlen, ein JDK von mindestens Version 8 zu verwenden, da die Builds für Version 6 und 7 die Verwendung von unverschlüsseltem HTTP erfordern. Seine Unterstützung ist nur für das Ausführen von Tests gegen diese JDK-Version gedacht und kann Sie den Angriffen des Menschen in den Mitte aussetzen. Daher sollten diese Builds vermieden werden. Byte Buddy wird derzeit auf Versionen 6 und hinauf des JDK auf CI -Servern getestet.
Bitte verwenden Sie Githubs Ausgaber -Tracker für die Meldung von Fehler. Geben Sie bei Code Testfälle an, die die Funktionalität Ihrer Funktionen beweisen oder eine Fehlerbehebung demonstrieren. Stellen Sie außerdem sicher, dass Sie keine vorhandenen Testfälle brechen. Wenn möglich, nehmen Sie sich bitte die Zeit, um eine Dokumentation zu schreiben. Für Feature -Anfragen oder allgemeine Feedback können Sie auch den Problemverfolger verwenden oder uns in unserer Mailingliste kontaktieren.
Die Arbeit am Byte Buddy ist auch dank einer Reihe von Unterstützern möglich, die regelmäßig Ressourcen und Aufmerksamkeit für das Projekt gewidmet sind. Bitte nehmen Sie sich Zeit, um sich diese Unterstützer und ihre Angebote anzusehen.