У випадку шаблону дизайну проксі , яка різниця між динамічним проксі JDK від API динамічного генерування коду сторонніх виробників, таким як CGLib ?
Яка різниця між використанням як підходів, так і коли слід віддавати перевагу одному перед іншим?
У випадку шаблону дизайну проксі , яка різниця між динамічним проксі JDK від API динамічного генерування коду сторонніх виробників, таким як CGLib ?
Яка різниця між використанням як підходів, так і коли слід віддавати перевагу одному перед іншим?
Відповіді:
Проксі-сервер JDK Dynamic може проксі лише через інтерфейс (тому ваш цільовий клас повинен реалізувати інтерфейс, який потім також реалізується класом проксі).
CGLIB (і javassist) може створити проксі за допомогою підкласифікації. У цьому випадку проксі стає підкласом цільового класу. Не потрібно інтерфейсів.
Тож проксі-сервери Java Dynamic можуть проксі: public class Foo implements iFoo
де CGLIB може проксі:public class Foo
Редагувати:
Я мушу зазначити, що оскільки javassist і CGLIB використовують проксі підкласифікацією, це причина, що ви не можете оголосити остаточні методи або зробити клас остаточним під час використання фреймворків, які покладаються на це. Це не дозволить цим бібліотекам дозволити підкласифікувати свій клас та змінити ваші методи.
Відмінності у функціональності
Проксі-сервери 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 вже не знаходиться в активному розвитку.
Динамічний проксі: Динамічна реалізація інтерфейсів під час виконання за допомогою API JDK Reflection .
Приклад: Spring використовує динамічні проксі-сервери для транзакцій наступним чином:
Згенерований проксі надходить на бін. Це додає транснаціональній поведінці зерна. Тут проксі динамічно генерується під час виконання, використовуючи JDK Reflection API.
Коли програма буде зупинена, проксі буде знищений, і ми матимемо лише інтерфейс та бін у файловій системі.
У наведеному вище прикладі ми маємо інтерфейс. Але в більшості реалізація інтерфейсу не найкраща. Тож bean не реалізує інтерфейс, і в цьому випадку ми використовуємо успадкування:
Для того, щоб генерувати такі проксі, Spring використовує сторонні бібліотеки під назвою CGLib .
CGLib ( C ode G eneration Lib rary ) побудований на базі ASM , в основному це використовується для створення проксі-розширювальних бобів та додає поведінку біна в методах проксі.
Spring AOP використовує або динамічні проксі-сервери JDK, або CGLIB, щоб створити проксі для даного цільового об'єкта. (Динамічні проксі-сервери JDK бажані, коли у вас є вибір).
Якщо цільовий об'єкт, який підлягає проксі, реалізує щонайменше один інтерфейс, тоді буде використаний динамічний проксі JDK. Усі інтерфейси, реалізовані цільовим типом, будуть проксі. Якщо цільовий об'єкт не реалізує жодних інтерфейсів, тоді буде створений проксі CGLIB.
Якщо ви хочете змусити використовувати CGLIB-проксі (наприклад, для проксі кожного методу, визначеного для цільового об'єкта, а не лише тих, що реалізовані його інтерфейсами), ви можете це зробити. Однак слід враховувати деякі питання:
остаточні методи не можна рекомендувати, оскільки їх не можна перекрити.
Вам потрібні бінарні файли CGLIB 2 на вашому класі, тоді як динамічні проксі-сервери доступні в JDK. Весна автоматично попередить вас, коли їй потрібен CGLIB, а класи бібліотеки CGLIB не знайдені на шляху.
Конструктор вашого проксі-об'єкта буде викликаний двічі. Це природний наслідок проксі-моделі CGLIB, згідно з якою для кожного проксі-об'єкта генерується підклас. Для кожного проксі-екземпляра створюються два об'єкти: власне проксі-об'єкт та екземпляр підкласу, який реалізує поради. Така поведінка не проявляється при використанні проксі-серверів JDK. Зазвичай виклик конструктора проксі-класу два рази не є проблемою, оскільки зазвичай проводяться лише завдання, а в конструкторі реальна логіка не реалізована.