Чим відрізняється динамічний проксі JDK від CGLib?


147

У випадку шаблону дизайну проксі , яка різниця між динамічним проксі JDK від API динамічного генерування коду сторонніх виробників, таким як CGLib ?

Яка різниця між використанням як підходів, так і коли слід віддавати перевагу одному перед іншим?


3
Отримайте код тут: < gist.github.com/ksauzz/1563486 >. У cglib ви можете створити як проксі класу, так і інтерфейс проксі. Spring використовує CGlib за замовчуванням, тоді як AspectJ використовує проксі-сервер Java. Читайте також це: jnb.ociweb.com/jnb/jnbNov2005.html ;)
Subhadeep Ray

Відповіді:


185

Проксі-сервер JDK Dynamic може проксі лише через інтерфейс (тому ваш цільовий клас повинен реалізувати інтерфейс, який потім також реалізується класом проксі).

CGLIB (і javassist) може створити проксі за допомогою підкласифікації. У цьому випадку проксі стає підкласом цільового класу. Не потрібно інтерфейсів.

Тож проксі-сервери Java Dynamic можуть проксі: public class Foo implements iFooде CGLIB може проксі:public class Foo

Редагувати:

Я мушу зазначити, що оскільки javassist і CGLIB використовують проксі підкласифікацією, це причина, що ви не можете оголосити остаточні методи або зробити клас остаточним під час використання фреймворків, які покладаються на це. Це не дозволить цим бібліотекам дозволити підкласифікувати свій клас та змінити ваші методи.


Дякую..!! але було б корисно, якщо ви можете надати мені один приклад коду (або Посилання), щоб проілюструвати використання в іншому випадку використання іншого .. !!!
KDjava

1
Зауважте, що проксі-сервери JDK насправді припиняють проксі для IFoo, а не для Foo. Це досить важлива відмінність. Також проксі-сервери - це повнокласні підкласи - скористайтеся цим! Використовуйте фільтри лише для проксі-методів, які вас цікавлять, і використовуйте згенерований клас безпосередньо.
lscoughlin

9
Слід також зазначити, що створення підкласу CGLib вимагає достатнього знання про суперкласс, щоб можна було викликати правильний конструктор за допомогою правильних аргументів. На відміну від проксі-інтерфейсу, який не хвилює конструкторів. Це робить роботу з проксі-серверами CGLib менш "автоматичною", ніж проксі-сервери JDK. Ще одна відмінність - у вартості «стека». Проксі-сервер JDK завжди містить додаткові кадри стека за виклик, тоді як CGLib може не коштувати додаткових фреймів стека. Це стає все більш актуальним, чим складніший додаток отримує (адже чим більший стек, тим більше споживаних ниток пам'яті).
Рей

1
cglib не може прокси-остаточні методи, але не викине виключення gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80
Muhammad Hewedy

Так, CGLIB просто ігнорує остаточні методи.
yashjain12yj

56

Відмінності у функціональності

  • Проксі-сервери JDK дозволяють реалізувати будь-який набір інтерфейсів під час підкласифікації Object. Будь-який метод інтерфейсу, плюс Object::hashCode, Object::equalsа Object::toStringпотім передається до InvocationHandler. Додатково java.lang.reflect.Proxyреалізований стандартний інтерфейс бібліотеки .

  • cglib дозволяє реалізувати будь-який набір інтерфейсів, підкласируючи будь-який не остаточний клас. Крім того, методи можуть бути відмінені необов'язково, тобто не всі не абстрактні методи потрібно перехоплювати. Крім того, існують різні способи реалізації методу. Він також пропонує InvocationHandlerклас (в іншому пакеті), але він також дозволяє викликати супер методи, використовуючи більш просунуті перехоплювачі, як, наприклад, a MethodInterceptor. Крім того, cglib може покращити продуктивність завдяки спеціалізованим перехопленням FixedValue. Я колись написав резюме різних перехоплювачів для cglib .

Різниці в продуктивності

Проксі-сервери JDK реалізовані досить наївно лише з одним диспетчером перехоплення - InvocationHandler. Для цього потрібна віртуальна відправка методу до реалізації, яку не завжди можна накреслити. Cglib дозволяє створювати спеціалізований байт-код, який іноді може підвищити продуктивність. Ось декілька порівнянь щодо реалізації інтерфейсу з 18 методами заглушки:

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

Час відзначається в наносекундах зі стандартним відхиленням в дужках. Більш детальну інформацію ви можете знайти на еталоні в підручнику Byte Buddy, де Byte Buddy є більш сучасною альтернативою cblib. Також зауважте, що cglib вже не знаходиться в активному розвитку.


2
Чому весняна документація сприяє наближенню JDK над кегл, враховуючи переваги роботи останнього? docs.spring.io/spring/docs/2.5.x/reference/…
P4ndaman

2
Кліб є зовнішньою залежністю і наразі не підтримується. Покладатися на сторонне програмне забезпечення - це завжди азартна гра, тому найкраще, коли на неї покладається якомога менше людей.
Рафаель Вінтерхалтер

У своєму блозі ви кажете: "Однак ви повинні бути обережними, викликаючи метод на об'єкті проксі, який поставляється із методом InvocationHandler # invoke. Усі виклики цього методу будуть відправлені з тим же InvocationHandler і, отже, можуть призвести до нескінченного циклу . " Що ви маєте на увазі?
Корай Тугай

Якщо ви викликаєте метод на об'єкті проксі, будь-який дзвінок буде переадресований, наш обробник виклику. Якщо будь-який обробник виклику делегує делегування виклику об'єкту, відбувається згадана рекурсія.
Рафаель Вінтерхалтер

Привіт, Рафаеле, повідомлення, яке не стосується вашої відповіді, я повідомлю вас про редагування, зроблене 5 років тому . Оскільки cglib, очевидно, все ще має місце в 2019 році, і він не демонструє жодної арештованої розробки в своєму readme, я вилучив вашу заяву з уривку тегу. Не соромтеся покращити опис / уривок тегів, якщо є що-небудь релевантне для згадування.
Cœur

28

Динамічний проксі: Динамічна реалізація інтерфейсів під час виконання за допомогою API JDK Reflection .

Приклад: Spring використовує динамічні проксі-сервери для транзакцій наступним чином:

введіть тут опис зображення

Згенерований проксі надходить на бін. Це додає транснаціональній поведінці зерна. Тут проксі динамічно генерується під час виконання, використовуючи JDK Reflection API.

Коли програма буде зупинена, проксі буде знищений, і ми матимемо лише інтерфейс та бін у файловій системі.


У наведеному вище прикладі ми маємо інтерфейс. Але в більшості реалізація інтерфейсу не найкраща. Тож bean не реалізує інтерфейс, і в цьому випадку ми використовуємо успадкування:

введіть тут опис зображення

Для того, щоб генерувати такі проксі, Spring використовує сторонні бібліотеки під назвою CGLib .

CGLib ( C ode G eneration Lib rary ) побудований на базі ASM , в основному це використовується для створення проксі-розширювальних бобів та додає поведінку біна в методах проксі.

Приклади проксі-сервера JDK Dynamic та CGLib

Весна реф


5

З весняної документації :

Spring AOP використовує або динамічні проксі-сервери JDK, або CGLIB, щоб створити проксі для даного цільового об'єкта. (Динамічні проксі-сервери JDK бажані, коли у вас є вибір).

Якщо цільовий об'єкт, який підлягає проксі, реалізує щонайменше один інтерфейс, тоді буде використаний динамічний проксі JDK. Усі інтерфейси, реалізовані цільовим типом, будуть проксі. Якщо цільовий об'єкт не реалізує жодних інтерфейсів, тоді буде створений проксі CGLIB.

Якщо ви хочете змусити використовувати CGLIB-проксі (наприклад, для проксі кожного методу, визначеного для цільового об'єкта, а не лише тих, що реалізовані його інтерфейсами), ви можете це зробити. Однак слід враховувати деякі питання:

остаточні методи не можна рекомендувати, оскільки їх не можна перекрити.

Вам потрібні бінарні файли CGLIB 2 на вашому класі, тоді як динамічні проксі-сервери доступні в JDK. Весна автоматично попередить вас, коли їй потрібен CGLIB, а класи бібліотеки CGLIB не знайдені на шляху.

Конструктор вашого проксі-об'єкта буде викликаний двічі. Це природний наслідок проксі-моделі CGLIB, згідно з якою для кожного проксі-об'єкта генерується підклас. Для кожного проксі-екземпляра створюються два об'єкти: власне проксі-об'єкт та екземпляр підкласу, який реалізує поради. Така поведінка не проявляється при використанні проксі-серверів JDK. Зазвичай виклик конструктора проксі-класу два рази не є проблемою, оскільки зазвичай проводяться лише завдання, а в конструкторі реальна логіка не реалізована.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.