Juste par curiosité, existe-t-il des projets open source (stables) pour la génération de code Java d'exécution autres que cglib? Et pourquoi devrais-je les utiliser?
CGLIB et presque toutes les autres bibliothèques sont construites sur ASM, qui agit à un niveau très bas. Il s’agit là d’une vitrine pour la plupart des gens, car vous devez comprendre le code des octets et un peu de JVMS pour l’utiliser correctement. Mais maîtriser l'ASM est très certainement très intéressant. Notez cependant que s'il existe un excellent guide ASM 4 , la documentation javadoc peut être très concise dans certaines parties de l'API si elle est présente, mais elle est en train d'être améliorée. Il suit de près les versions de la machine virtuelle Java pour prendre en charge les nouvelles fonctionnalités.
Cependant, si vous avez besoin d'un contrôle total, l'ASM est votre arme de choix.
Ce projet est régulièrement mis à jour. Au moment de cette édition, la version 5.0.4 est sortie le 15 mai 2015.
Byte Buddy est une bibliothèque plutôt nouvelle mais fournit toutes les fonctionnalités fournies par CGLIB ou Javassist et bien plus encore. Octet Copain peut être entièrement personnalisé jusqu'au niveau du code octet et est livré avec un langage expressif spécifique à un domaine qui permet un code très lisible.
Taper des rappels sécurisés
Les conseils de Javassist ou le code d'instrumentation personnalisé sont basés sur le code dans un
String
name__, ce qui rend le contrôle de type impossible et le débogage impossible dans ce code, tandis que ByteBuddy permet d'écrire ceux avec pure Java et applique ainsi les contrôles de type et permet le débogage.
Annotation pilotée (flexible)
Les rappels utilisateur peuvent être configurés avec des annotations permettant de recevoir les paramètres souhaités dans le rappel.
Disponible en tant qu'agent
Le constructeur d'agents astucieux permet à ByteBuddy d'être utilisé comme agent pur ou comme agent de liaison. Il permet genre différent
Le principal inconvénient est peut-être que l’API est un peu prolixe pour un débutant, mais elle est conçue comme une API optionnelle sous la forme d’un DSL de génération de proxy; il n'y a pas de défauts magiques ou douteux. Lors de la manipulation de code octet, c'est probablement le choix le plus sûr et le plus raisonnable. De plus, avec plusieurs exemples et un gros tutoriel, ce n'est pas un problème réel.
En octobre 2015, ce projet a reçu le choix de Oracle Duke . À ce stade, il vient d’atteindre le 1.0.0 jalon , ce qui est tout un exploit.
Notez que mockito a remplacé CGLIB par Byte Buddy dans la version 2.1.0.
Le javadoc de Javassist est bien meilleur que celui de CGLIB. L'API d'ingénierie de classe est OK, mais Javassist n'est pas parfait non plus. En particulier, ProxyFactory
name__, qui est l'équivalent du Enhancer
de CGLIB, présente également des inconvénients, pour en énumérer quelques-uns:
ClassloaderProvider
est un champ statique à la place, il s'applique alors à toutes les instances du même chargeur de classesProxyFactory
name__.Du côté des aspects, on peut injecter du code dans un proxy, mais cette approche dans Javassist est limitée et un peu sujette aux erreurs:
Javassist est également reconnu pour être plus lent que Cglib. Ceci est principalement dû à son approche consistant à lire les fichiers de classe au lieu de lire les classes chargées comme le fait CGLIB. Et le mise en œuvre lui-même est difficile à lire pour être juste; si vous devez apporter des modifications au code javassiste, il y a beaucoup de chances de casser quelque chose.
Javassist souffrait également d'inactivité. Leur déménagement vers github circa 201 semble s'être avéré utile car il montre des commits réguliers et suscite des demandes de la part de la communauté.
Ces limitations sont toujours présentes dans la version 3.17.1. La version a été remplacée par la version 3.20.0, mais il semble que Javassist puisse toujours avoir des problèmes de prise en charge de Java 8.
JiteScript semble être un nouveau morceau de DSL pour ASM, basé sur la dernière version ASM (4.0). Le code a l'air propre.
Mais le projet en est encore à ses balbutiements, de sorte que le comportement de l'API/du comportement peut changer, en plus de la documentation. Et mises à jour rares sinon abandonnées.
Il s’agit d’un outil relativement nouveau, mais il offre l’API de loin la meilleure humaine. Il permet différents types de mandataires tels que les mandataires de sous-classe (approche cglib) ou le tissage ou la délégation.
Bien que celui-ci soit plutôt rare, aucune information n’est disponible si cela fonctionne bien. Il y a tellement de cas à traiter avec le bytecode.
AspectJ est un outil très puissant pour la programmation orientée aspect (uniquement). AspectJ manipule le code d'octet pour atteindre ses objectifs, de sorte que vous puissiez atteindre vos objectifs avec ce code. Cependant, cela nécessite une manipulation lors de la compilation; offre de printemps tissage au temps de chargement via un agent depuis la version 2.5 , 4.1.x .
n mot sur CGLIB qui a été mis à jour depuis que cette question a été posée.
CGLIB est assez rapide, c’est l’une des principales raisons pour lesquelles elle existe toujours, ainsi que le fait que CGLIB a fonctionné presque mieux que n’importe quelle solution de remplacement jusqu’à présent (2014-2015).
En règle générale, les bibliothèques qui permettent la réécriture des classes au moment de l'exécution doivent éviter de charger des types avant la réécriture de la classe correspondante. Par conséquent, ils ne peuvent pas utiliser l'API de réflexion Java qui requiert le chargement de tout type utilisé en réflexion. Au lieu de cela, ils doivent lire les fichiers de classe via IO (qui interrompt les performances). Cela rend par exemple Javassist ou Proxetta nettement plus lent que Cglib qui lit simplement les méthodes via l’API de réflexion et les remplace.
Cependant, CGLIB n'est plus en développement actif. Il y a eu des versions récentes, mais ces modifications ont été jugées insignifiantes par beaucoup et la plupart des gens ne sont jamais passés à la version 3, car CGLIB a introduit quelques bugs graves dans les dernières versions, ce qui n'a pas vraiment permis de gagner en confiance. La version 3.1 corrigea un grand nombre des problèmes de la version 3.0 (depuis la version 4.0.3, le framework Spring reconditionne version 3.1 ).
En outre, le code source de CGLIB est plutôt de mauvaise qualité tel que nous ne voyons pas de nouveaux développeurs rejoindre le projet CGLIB. Pour une impression de l'activité de CGLIB, voir leur liste de diffusion .
Notez que suite à proposition sur la liste de diffusion guice , CGLIB est maintenant disponible sur github pour permettre à la communauté de mieux aider le projet, il semble fonctionner (plusieurs commits et demandes pull, ci, mis à jour), mais la plupart des préoccupations subsistent.
Ils travaillent actuellement sur la version 3.2.0 et concentrent leurs efforts sur Java 8, mais jusqu'à présent, les utilisateurs qui souhaitent que le support de Java 8 utilisent des astuces lors de la compilation. . Mais les progrès sont très lents.
Et CGLIB est toujours connu pour être en proie à une fuite de mémoire PermGen. Mais d'autres projets n'ont peut-être pas été testés au combat depuis tant d'années.
Celui-ci n'est pas d'exécution, bien sûr, mais constitue une partie importante de l'écosystème, et la plupart des utilisations de génération de code n'ont pas besoin de création d'exécution.
Cela a commencé avec Java 5 fourni avec l'outil de ligne de commande distinct pour le traitement des annotations: apt
name__, et à partir du Java 6, le traitement des annotations est intégré au compilateur Java. .
À un moment donné, vous deviez explicitement passer le processeur, maintenant avec l'approche ServiceLoader
(ajoutez simplement ce fichier META-INF/services/javax.annotation.processing.Processor
au jar), le compilateur peut détecter automatiquement le processeur d'annotation.
Cette approche lors de la génération de code présente également des inconvénients et nécessite beaucoup de travail et de compréhension du langage Java et non du bytecode. Cette API est un peu lourde, et comme on est plugin dans le compilateur, il faut faire très attention à faire de ce code le message d'erreur le plus résilient et le plus convivial.
Le plus gros avantage ici est que cela évite une autre dépendance au moment de l'exécution, vous éviterez peut-être une fuite de mémoire permgen. Et on a le contrôle total sur le code généré.
Dans 2002 , CGLIB a défini un nouveau standard permettant de manipuler facilement le bytecode. De nombreux outils et méthodologies (IC, couverture, TDD, etc.) dont nous disposons aujourd'hui n'étaient pas disponibles ou n'étaient pas à maturité à cette époque. CGLIB a réussi à être pertinent pendant plus d'une décennie; c'est un exploit assez décent. C'était rapide et avec une API facile à utiliser que de manipuler directement les opcodes.
Il définissait une nouvelle norme en matière de génération de code, mais ce n'est plus le cas aujourd'hui, car l'environnement et les exigences ont changé, tout comme les normes et les objectifs.
La machine virtuelle Java a changé et changera dans les versions récentes et futures Java (7/8/9/10) (invokedynamic, méthodes par défaut, types de valeur, etc.). ASM a mis à jour régulièrement son API et ses internes pour suivre ces modifications, mais CGLIB et d’autres n’ont pas encore utilisé ces logiciels.
Bien que le traitement des annotations gagne du terrain, il n'est pas aussi flexible que la génération à l'exécution.
À partir de 2015, Byte Buddy - relativement nouveau sur la scène - offre la plus convaincante vente = points pour la génération d'exécution. Un taux de mise à jour décent, et l'auteur a une connaissance intime des éléments internes du code Java.
Je préfère raw ASM , qui, à mon avis, est utilisé par cglib de toute façon. C'est un niveau bas, mais la documentation est brillante, et une fois que vous vous y serez habitué, vous volerez.
Pour répondre à votre deuxième question, vous devez utiliser la génération de code lorsque vos proxy et réflexions dynamiques commencent à se sentir un peu pavés et que vous avez besoin d’une solution solide. Dans le passé, j’ai même ajouté une étape de génération de code dans le processus de construction d’Eclipse, ce qui me permettait de générer des rapports de compilation de tout et de rien.
Si vous devez créer des procurations, consultez commons-proxy -, qui utilise à la fois CGLIB et Javassit.
Je pense que c'est plus logique d'utiliser Javassist au lieu de cglib. Par exemple. Contrairement à cglib, javasist fonctionne parfaitement avec des pots signés. En outre, le projet Hibernate a décidé de ne plus utiliser cglib au profit de Javassist .
CGLIB a été conçu et mis en œuvre il y a plus de dix ans à l'époque des AOP et des ORM. Actuellement, je ne vois aucune raison de l’utiliser et je ne maintiens plus cette bibliothèque (sauf les corrections de bugs pour mes applications héritées) . __.Il devrait être simple d'implémenter la même fonctionnalité via n'importe quel langage de script JVM, par exemple sensationnel.