В чем разница между динамическим прокси JDK и CGLib?


147

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

В чем разница между использованием обоих подходов и когда один предпочитает один другому?


3
Получите код здесь: < gist.github.com/ksauzz/1563486 >. В cglib вы можете создавать как прокси класса, так и интерфейсный прокси. Spring по умолчанию использует CGlib, а AspectJ использует прокси Java. Прочитайте это также: jnb.ociweb.com/jnb/jnbNov2005.html ;)
Субхадип Рэй

Ответы:


185

JDK Динамический прокси может прокси-сервер только через интерфейс (поэтому вашему целевому классу необходимо реализовать интерфейс, который затем также реализуется прокси-классом).

CGLIB (и javassist) могут создавать прокси путем создания подклассов. В этом сценарии прокси становится подклассом целевого класса. Нет необходимости в интерфейсах.

Таким образом, прокси-серверы Java Dynamic могут прокси: public class Foo implements iFooгде CGLIB может прокси:public class Foo

РЕДАКТИРОВАТЬ:

Я должен упомянуть, что, поскольку javassist и CGLIB используют прокси-серверы по подклассам, это является причиной, по которой вы не можете объявить финальные методы или сделать класс финальным при использовании фреймворков, которые полагаются на это. Это помешало бы этим библиотекам создавать подклассы для вашего класса и переопределять ваши методы.


Спасибо..!! но было бы полезно, если бы вы могли дать мне один пример кода (или ссылку), чтобы проиллюстрировать использование одного над другим в некоторых случаях ... !!!
КДжава

1
Обратите внимание, что прокси-серверы JDK фактически используют прокси для IFoo, а не для Foo вообще. Это довольно важное различие. Кроме того, прокси-серверы cglib являются полными подклассами - воспользуйтесь этим! Используйте фильтры только для тех прокси-методов, которые вам нужны, и напрямую используйте сгенерированный класс.
lscoughlin

9
Также следует отметить, что создание подкласса CGLib требует достаточных знаний о суперклассе, чтобы иметь возможность вызывать правильный конструктор с правильными аргументами. В отличие от интерфейсного прокси, который не заботится о конструкторах. Это делает работу с прокси CGLib менее «автоматической», чем прокси JDK. Другое различие заключается в стоимости «стека». JDK-прокси всегда получает дополнительные кадры стека за вызов, в то время как CGLib может не стоить дополнительных кадров стека. Это становится все более актуальным, чем сложнее становится приложение (поскольку чем больше стек, тем больше потоков памяти потребляют).
Рэй

1
CGLIB не может прокси окончательные методы, но не выбросит исключение gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80
Мухаммад 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 - более современная альтернатива cglib. Также обратите внимание, что cglib больше не находится в активной разработке.


2
Почему весенняя документация предпочитает JDK-прокси по сравнению с cglib, учитывая преимущества производительности последнего? docs.spring.io/spring/docs/2.5.x/reference/…
P4ndaman

2
Cglib является внешней зависимостью и в настоящее время не поддерживается. Опора на стороннее программное обеспечение - это всегда азартная игра, поэтому лучше всего, когда на него полагаются как можно меньше людей.
Рафаэль Винтерхальтер

В своем блоге вы говорите: «Однако вы должны быть осторожны при вызове метода для прокси-объекта, который поставляется с методом invocationHandler # invoke. Все вызовы этого метода будут отправлены с одним и тем же InvocationHandler и, следовательно, могут привести к бесконечному циклу «. Что вы имеете в виду?
Корай Тугай

Если вы вызываете метод для прокси-объекта, любой вызов направляется через наш обработчик вызовов. Если какой-либо обработчик вызова вызывает делегатов для вызова объекта, происходит упомянутая рекурсия.
Рафаэль Винтерхальтер

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

28

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

Пример: Spring использует динамические прокси для транзакций следующим образом:

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

Сгенерированный прокси приходит поверх bean-компонента. Это добавляет транснациональное поведение к бобу. Здесь прокси генерируется динамически во время выполнения, используя JDK Reflection API.

Когда приложение остановлено, прокси будет уничтожен, и у нас будет только интерфейс и бин в файловой системе.


В приведенном выше примере у нас есть интерфейс. Но в большинстве случаев реализация интерфейса не самая лучшая. Таким образом, bean-компонент не реализует интерфейс, в этом случае мы используем наследование:

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

Для генерации таких прокси Spring использует стороннюю библиотеку под названием CGLib .

CGLIB ( С одой G eneration Lib окон- чательно) построен на вершине ASM , это в основном используется генерировать прокси - простирающуюся компонент и добавляет поведение фасоли в прокси - методов.

Примеры для JDK Динамический прокси и CGLib

Весенний реф


5

Из весенней документации :

Spring AOP использует динамические прокси JDK или CGLIB для создания прокси для данного целевого объекта. (Динамические прокси JDK предпочтительны, когда у вас есть выбор).

Если целевой объект для прокси реализует хотя бы один интерфейс, то будет использоваться динамический прокси JDK. Все интерфейсы, реализованные целевым типом, будут проксированы. Если целевой объект не реализует никаких интерфейсов, будет создан прокси-сервер CGLIB.

Если вы хотите принудительно использовать прокси-сервер CGLIB (например, прокси-сервер для всех методов, определенных для целевого объекта, а не только для тех, которые реализованы его интерфейсами), вы можете сделать это. Тем не менее, есть несколько вопросов для рассмотрения:

окончательные методы не могут быть рекомендованы, так как они не могут быть переопределены.

Вам понадобятся двоичные файлы CGLIB 2 на вашем пути к классам, в то время как динамические прокси доступны с JDK. Spring автоматически предупредит вас, когда ему нужен CGLIB, и классы библиотеки CGLIB не найдены в пути к классам.

Конструктор вашего прокси-объекта будет вызываться дважды. Это естественное следствие прокси-модели CGLIB, согласно которой для каждого прокси-объекта создается подкласс. Для каждого экземпляра прокси создаются два объекта: фактический объект прокси и экземпляр подкласса, который реализует рекомендацию. Такое поведение не проявляется при использовании прокси-серверов JDK. Обычно, вызов конструктора прокси-типа дважды не является проблемой, поскольку обычно выполняются только присваивания, и в конструкторе не реализована реальная логика.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.