В чем разница между JDK dynamic proxy и CGLib?


в случае Шаблон Проектирования Прокси, в чем разница между динамический Прокси JDK и сторонние API генерации динамического кода, такие как CGLib?

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

4 107

4 ответа:

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

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

таким образом, динамические прокси Java могут прокси: public class Foo implements iFoo где CGLIB может прокси:public class Foo

EDIT:

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

функциональные возможности

  • прокси JDK позволяют реализовать любой набор интерфейсов при создании подклассов java.lang.reflect.Proxy. Любой метод интерфейса, плюсObject::hashCode,Object::equals и Object::toString затем перенаправляется в InvocationHandler.

  • 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's tutorial, где Byte Buddy является более современной альтернативой cglib. Кроме того, обратите внимание, что cglib больше не находится в активной разработке.

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

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

enter image description here

сгенерированный прокси приходит на вершине Боба. Это добавляет транснациональное поведение к Бобу. Здесь прокси генерирует динамически во время выполнения с помощью JDK Reflection API.

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


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

enter image description here

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

CGLib (Cода Generation Library) построен на вершине ПКР, это в основном используется компонент расширения прокси-сервера и добавляет поведение компонента в методы прокси-сервера.

примеры для JDK Dynamic proxy и CGLib

Весна ref

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

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

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

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

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

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

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