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

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

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

Ответ 1

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

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

Итак, Java Dynamic прокси могут прокси: public class Foo implements iFoo где CGLIB может прокси: public class Foo

EDIT:

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

Ответ 2

Отличия в функциональности

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

  • cglib позволяет вам реализовать любой набор интерфейсов, в то же время создавая подклассы для любого не финального класса. Кроме того, методы могут быть переопределены по желанию, то есть не все неабстрактные методы должны быть перехвачены. Кроме того, существуют разные способы реализации метода. Он также предлагает класс InvocationHandler (в другом пакете), но он также позволяет вызывать супер-методы с использованием более сложных перехватчиков, например, 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 больше не находится в активной разработке.

Ответ 3

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

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

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

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

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


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

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

Чтобы создать такие прокси, Spring использует стороннюю библиотеку с именем CGLib.

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

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

Spring ref

Ответ 4

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

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

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

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

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

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

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