web-dev-qa-db-fra.com

Existe-t-il des alternatives à cglib?

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?

54
Mauli

ASMJava-asm

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.

Copain d'octetCopain d'octet

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.

  • Il supporte toutes les versions de bytecode de la machine virtuelle Java, y compris Java 8 modifications sémantiques de certains opcodes concernant les méthodes par défaut.
  • ByteBuddy ne semble pas souffrir des inconvénients des autres bibliothèques
  • Hautement configurable
  • Assez rapide ( benchmarkcode )
  • Tapez une API sécurisée
  • Taper des rappels sécurisés

    Les conseils de Javassist ou le code d'instrumentation personnalisé sont basés sur le code dans un Stringname__, 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

  • Très bien documenté
  • Beaucoup d'exemples
  • Code propre, couverture de test d'environ 94%
  • Prise en charge d'Android DEX

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.

Javassistjavassist

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, ProxyFactoryname__, qui est l'équivalent du Enhancerde CGLIB, présente également des inconvénients, pour en énumérer quelques-uns:

  • La méthode bridge n'est pas entièrement prise en charge (c'est-à-dire celle générée pour les types de retour covariants)
  • ClassloaderProviderest un champ statique à la place, il s'applique alors à toutes les instances du même chargeur de classes
  • Les noms personnalisés auraient pu être les bienvenus (avec des chèques pour les bocaux signés)
  • Il n'y a pas de point d'extension et presque toutes les méthodes d'intérêt sont privées, ce qui est fastidieux si nous voulons changer certains comportements.
  • Bien que Javassist prenne en charge les attributs d’annotation dans les classes, ils ne sont pas pris en charge dans ProxyFactoryname__.

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:

  • le code d'aspect est écrit dans un _Java chaîne qui est compilé dans les opcodes
  • pas de vérification de type
  • pas de génériques
  • pas de lambda
  • pas de boxe automatique

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

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.

Proxettajodd

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.

AspectJaspectj

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 .

CGLIBcglib

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.

traitement des annotations à la compilationtraitement des annotations

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: aptname__, 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é.

Conclusion

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.

102
Brice

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.

10
CurtainDog

Javassist .

Si vous devez créer des procurations, consultez commons-proxy -, qui utilise à la fois CGLIB et Javassit.

10
Bozho

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 .

4
FoxyBOA

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.

0
jbaliuka