web-dev-qa-db-fra.com

Quelle est la différence entre le proxy dynamique JDK et CGLib?

Dans le cas où modèle de conception de proxy , quelle est la différence entre proxy dynamique du JDK et les API de génération de code dynamique tierces telles que CGLib ?

Quelle est la différence entre les deux approches et quand faut-il en préférer une?

121
KDjava

Le proxy dynamique JDK peut uniquement utiliser un proxy par interface (votre classe cible doit donc implémenter une interface, qui est également implémentée par la classe proxy).

CGLIB (et javassist) peuvent créer un proxy en sous-classant. Dans ce scénario, le proxy devient une sous-classe de la classe cible. Pas besoin d'interfaces.

Donc, Java, les proxy dynamiques peuvent utiliser un proxy: public class Foo implements iFoo, Où CGLIB peut utiliser un proxy: public class Foo

MODIFIER:

Je devrais mentionner que javassist et CGLIB utilisant proxy par sous-classe, vous ne pouvez pas déclarer les méthodes finales ni définir la classe final lorsque vous utilisez des cadres qui en dépendent. Cela empêcherait ces bibliothèques de permettre de sous-classer votre classe et de remplacer vos méthodes.

159
raphaëλ

Différences de fonctionnalités

  • Les mandataires JDK permettent d’implémenter n’importe quel jeu d’interfaces lors du sous-classement de Object. Toute méthode d'interface, plusObject::hashCode, Object::equals et Object::toString est ensuite transmis à un InvocationHandler . De plus, l'interface de bibliothèque standard Java.lang.reflect.Proxy est implémenté.

  • cglib vous permet d'implémenter n'importe quel jeu d'interfaces en sous-classant toute classe non finale. De plus, les méthodes peuvent éventuellement être remplacées, c'est-à-dire que toutes les méthodes non abstraites ne doivent pas nécessairement être interceptées. En outre, il existe différentes manières de mettre en œuvre une méthode. Il offre également une classe InvocationHandler (dans un paquet différent), mais permet également d'appeler des méthodes super en utilisant des intercepteurs plus avancés, comme par exemple un MethodInterceptor. De plus, cglib peut améliorer les performances par des interceptions spécialisées comme FixedValue. J'ai déjà écrit résumé de différents intercepteurs pour cglib .

Différences de performances

Les proxy JDK sont implémentés assez naïvement avec un seul répartiteur d'interception, le InvocationHandler. Cela nécessite une répartition de méthode virtuelle vers une implémentation qui ne peut pas toujours être en ligne. Cglib permet de créer un code octet spécialisé, ce qui peut parfois améliorer les performances. Voici quelques comparaisons pour l'implémentation d'une interface avec 18 méthodes stub:

            cglib                   JDK proxy
creation    804.000     (1.899)     973.650     (1.624)
invocation    0.002     (0.000)       0.005     (0.000)

Le temps est noté en nanosecondes avec un écart type entre accolades. Vous pouvez trouver plus de détails sur le repère dans le tutoriel de Byte Buddy , où Byte Buddy est une alternative plus moderne à cglib. Notez également que cglib n’est plus en développement actif.

50

Proxy dynamique: Implémentations dynamiques d'interfaces lors de l'exécution à l'aide de API de réflexion JDK .

Exemple: Spring utilise les mandataires dynamiques pour les transactions, comme suit:

enter image description here

Le proxy généré vient au-dessus du haricot. Cela ajoute un comportement transnational à la fève. Ici, le proxy génère de façon dynamique au moment de l'exécution à l'aide de l'API JDK Reflection.

Lorsqu'une application est arrêtée, le proxy sera détruit et nous n'aurons qu'une interface et un bean sur le système de fichiers.


Dans l'exemple ci-dessus, nous avons l'interface. Mais dans la plupart des cas, la mise en œuvre de l'interface n'est pas la meilleure. Donc, bean n'implémente pas d'interface. Dans ce cas, nous utilisons l'héritage:

enter image description here

Afin de générer de tels proxy, Spring utilise une bibliothèque tierce appelée CGLib .

CGLib ( [~ # ~] c [~ # ~] ode [~ # ~] g [~ # ~] l'énération Lib rary) est construite au-dessus de ASM , ceci est principalement utilisé pour générer le proxy en augmentant le bean et ajoute le comportement du bean dans les méthodes proxy.

Exemples pour JDK Dynamic Proxy et CGLib

référence du ressort

21
Premraj

d'après Spring documentation :

Spring AOP utilise les proxys dynamiques JDK ou CGLIB pour créer le proxy pour un objet cible donné. (Les procurations dynamiques JDK sont préférées chaque fois que vous avez le choix).

Si l'objet cible à proxy implémente au moins une interface, un proxy dynamique JDK sera utilisé. Toutes les interfaces implémentées par le type de cible seront mandatées. Si l'objet cible n'implémente aucune interface, un proxy CGLIB sera créé.

Si vous souhaitez forcer l'utilisation du proxy CGLIB (par exemple, pour proxy toutes les méthodes définies pour l'objet cible, pas uniquement celles implémentées par ses interfaces), vous pouvez le faire. Cependant, il y a quelques points à considérer:

les méthodes finales ne peuvent pas être conseillées, car elles ne peuvent pas être ignorées.

Vous aurez besoin des fichiers binaires CGLIB 2 sur votre chemin de classe, tandis que les proxys dynamiques sont disponibles avec le JDK. Spring vous avertit automatiquement lorsque vous avez besoin de CGLIB et que les classes de la bibliothèque CGLIB ne sont pas trouvées sur le chemin de classe.

Le constructeur de votre objet traité par proxy sera appelé deux fois. Ceci est une conséquence naturelle du modèle de proxy CGLIB dans lequel une sous-classe est générée pour chaque objet traité par proxy. Pour chaque instance proxy, deux objets sont créés: l'objet proxy réel et une instance de la sous-classe qui implémente l'avis. Ce comportement n'est pas présenté lors de l'utilisation de serveurs proxy JDK. Appeler deux fois le constructeur du type traité par proxy n'est pas un problème, car il n'y a généralement que des affectations et aucune logique réelle n'est implémentée dans le constructeur.

4
Taras Melnyk