Просто из любопытства, есть ли (стабильные) проекты с открытым исходным кодом для генерации кода Java во время выполнения, отличные от cglib? И почему я должен их использовать?
Существуют ли альтернативы cglib?
Ответ 1
ASM java-asm
CGLIB и почти все другие библиотеки построены поверх ASM, который сам действует на очень низком уровне. Это шоу-стоппер для большинства людей, так как вы должны понимать байтовый код и немного руководство ASM 4, в некоторой части API документация javadoc может быть очень кратким, если он присутствует вообще, но он улучшается. Он тесно следует версиям JVM для поддержки новых функций.
Однако, если вам нужен полный контроль, ASM - ваше оружие выбора.
Этот проект видит регулярные обновления; во время этого редактирования версия 5.0.4 была выпущена 15 мая 2015 года.
Byte Buddy byte-buddy
Byte Buddy - довольно новая библиотека, но предоставляет любую функциональность, предоставляемую CGLIB или Javassist, и многое другое. Байт Buddy может быть полностью настроен до уровня байтового кода и поставляется с выразительным языком, специфичным для домена, который позволяет очень читаемый код.
- Он поддерживает все версии байт-кода JVM, включая семантические изменения Java 8 некоторых опкодов относительно методов по умолчанию.
- ByteBuddy, похоже, не страдает от недостатков, которые другие библиотеки имеют
- Высоко настраиваемый
- Довольно быстро (эталон code)
- Тип безопасного безопасного API
-
Введите безопасные обратные вызовы
Совет Javassist или пользовательский код инструментария основаны на коде в простой
String
, поэтому проверка типа и отладка невозможна в этом коде, тогда как ByteBuddy позволяет писать те, у которых есть чистая Java, поэтому обеспечивает проверку типов и позволяет отлаживать. -
Аннотации, управляемые (гибкие)
Пользовательские обратные вызовы могут быть сконфигурированы с аннотациями, позволяющими получать требуемые параметры в обратном вызове.
-
Доступно как агент
Конструктор отличных агентов позволяет использовать ByteBuddy как чистый агент или агент-агент. Он позволяет различный вид
- Очень хорошо документировано
- Множество примеров
- Чистый код, ~ 94% покрытия
- Поддержка Android DEX
Возможно, главный недостаток API был бы немного подробным для новичков, но он был разработан как API-интерфейс, созданный как DSL-генерация прокси-сервера; нет никаких волшебных или сомнительных дефолтов. При манипулировании байтовым кодом это, вероятно, самый безопасный и самый разумный выбор. Также с несколькими примерами и большим учебником это не настоящая проблема.
В октябре 2015 года эти проекты получили награду за выбор Oracle Duke. В это время она достигла 1.0.0 рубежа, что является довольно достижением.
Обратите внимание, что mockito заменил CGLIB by Byte Buddy в версии 2.1.0.
Javassist javassist
Javadoc Javassist намного лучше, чем у CGLIB. API инженерии классов в порядке, но Javassist также не идеален. В частности, ProxyFactory
, который является эквивалентом CGLIB Enhancer
, также страдает некоторыми недостатками, просто перечисляя несколько:
- Метод Bridge не поддерживается полностью (т.е. тот, который генерируется для ковариантных типов возврата)
-
ClassloaderProvider
вместо этого статическое поле, тогда оно применяется ко всем экземплярам внутри одного загрузчика классов - Пользовательское именование может быть приветствованным (с проверкой подписанных баннеров)
- Нет точки расширения, и почти все интересующие ее методы являются частными, что громоздко, если мы хотим изменить какое-либо поведение.
- Пока Javassist предлагает поддержку атрибутов аннотации в классах, они не поддерживаются в
ProxyFactory
.
На стороне, ориентированной на аспект, можно ввести код в прокси-сервер, но этот подход в Javassist ограничен и подвержен ошибкам:
- Аспектный код написан в простой строке Java, которая скомпилирована в опкодах
- проверка типа
- нет дженериков
- no lambda
- no auto- (un) бокс
Также признан Javassist медленнее Cglib. Это в основном связано с его подходом к чтению файлов классов вместо чтения загруженных классов, таких как CGLIB. И реализация сама по себе трудно читать, чтобы быть честной; если требуется внести изменения в код Javassist, есть много шансов что-то сломать.
Джавассист также страдал от бездействия, их переход на github около 2013 года, по-видимому, оказался полезным, поскольку он показывает регулярные коммиты и тянет запросы от сообщества.
Эти ограничения по-прежнему сохраняются в версии 3.17.1. Версия была удалена до версии 3.20.0, но, похоже, у Javassist все еще могут быть проблемы с поддержкой Java 8.
JiteScript
JiteScript походит на новый кусочек приятного формирования DSL для ASM, основанный на последней версии ASM (4.0). Код выглядит чистым.
Но проект все еще находится в раннем возрасте, поэтому API/поведение может измениться, а документация ужасная. И обновления скудны, если не заброшены.
Proxetta jodd
Это довольно новый инструмент, но он предлагает лучший человеческий API. Он позволяет использовать разные типы прокси, такие как прокси-серверы подкласса (cglib-подход) или переплетение или делегирование.
Хотя этот вопрос довольно редок, информация не существует, если она работает хорошо. Есть так много углового случая, с которым приходится иметь дело с байт-кодом.
AspectJ aspectj
AspectJ - очень мощный инструмент для аспектно-ориентированного программирования (только). AspectJ управляет байтовым кодом для достижения своих целей, чтобы вы могли достичь своих целей. Однако это требует манипулирования во время компиляции; spring предлагает ткачество во время загрузки через агента с версии 2.5, 4.1.x.
CGLIB cglib
Слово о CGLIB, которое было обновлено после того, как этот вопрос был задан.
CGLIB довольно быстр, это одна из главных причин, по которой он все еще существует, а также тот факт, что CGLIB работал почти лучше любых альтернатив до сих пор (2014-2015).
Вообще говоря, библиотеки, позволяющие переписывать классы во время выполнения, должны избегать загрузки любых типов до перезаписывания соответствующего класса. Поэтому они не могут использовать API отражения Java, который требует, чтобы загружался любой тип, используемый в отражении. Вместо этого они должны читать файлы классов через IO (который является выключателем производительности). Это делает, например, Javassist или Proxetta значительно медленнее Cglib, который просто считывает методы с помощью API отражения и переопределяет их.
Однако CGLIB больше не находится в активной разработке. Были недавние выпуски, но эти изменения были заметны как незначительные для многих, и большинство людей никогда не обновлялось до версии 3, поскольку CGLIB ввел некоторые серьезные ошибки в последних выпускает то, что на самом деле не создавало уверенности. Версия 3.1 зафиксировала множество проблем версии 3.0 (начиная с версии 4.0.3 spring framework repackages версия 3.1).
Кроме того, исходный код CGLIB имеет скорее низкое качество, так что мы не видим, чтобы новые разработчики присоединились к проекту CGLIB. Для впечатления о активности CGLIB см. Их список рассылки.
Обратите внимание, что после предложения в списке рассылки guice, CGLIB теперь доступен на github, чтобы сообщество могло лучше помочь проекту, похоже, что оно работает (несколько коммитов и запросов на передачу, ci, обновленный maven), но большинство проблем по-прежнему остается.
В настоящее время работают над версией 3.2.0, и они сосредоточивают усилия на Java 8, но пока пользователи, которым нужна поддержка java 8, должны использовать трюки во время сборки. Но прогресс идет очень медленно.
И все еще известно, что CGLIB страдает от утечки памяти PermGen. Но другие проекты, возможно, не были испытаны на битву в течение стольких лет.
Компиляция аннотации времени Обработка annotation-processing
Это, конечно, не время выполнения, но является важной частью экосистемы, и большинство использования кода не требуют создания времени выполнения.
Это началось с Java 5, который поставляется с отдельным инструментом командной строки для обработки аннотаций: apt
, и начиная с обработки аннотации Java 6 интегрируется в компилятор Java.
В какой-то момент вам нужно было явно передать процессор, теперь с подходом ServiceLoader
(просто добавьте этот файл META-INF/services/javax.annotation.processing.Processor
в jar), компилятор может автоматически обнаружить процессор аннотации.
Такой подход при генерации кода имеет свои недостатки, и для этого требуется много работы и понимания языка Java, а не байт-кода. Этот API немного громоздкий, и поскольку один из них является плагином в компиляторе, нужно проявлять особую осторожность, чтобы сделать этот код наиболее устойчивым и удобным для пользователя сообщение об ошибке.
Самое большое преимущество здесь заключается в том, что он избегает другой зависимости во время выполнения, вы можете избежать утечки памяти в пермг. И один имеет полный контроль над сгенерированным кодом.
Заключение
В 2002 CGLIB определил новый стандарт для простого управления байт-кодом. Многие инструменты и методология (CI, охват, TDD и т.д.), Которые мы имеем сейчас, были недоступны или не были зрелыми в то время. CGLIB удалось учесть более десятилетия; это довольно приличное достижение. Это было быстро и с простым API, чтобы использовать, чем манипулировать опкодами напрямую.
Он определил новый стандарт в отношении генерации кода, но в настоящее время он больше не существует, потому что изменились среда и требования, поэтому есть стандарты и цели.
JVM изменился и изменится в последних и будущих версиях Java (7/8/9/10) (invokedynamic, методы по умолчанию, типы значений и т.д.). ASM регулярно обновлял свой API и внутренние компоненты, чтобы следить за этими изменениями, но CGLIB и другие еще не использовали их.
В то время как обработка аннотаций становится тягой, она не такая гибкая, как генерация времени выполнения.
По состоянию на 2015 год Byte Buddy - в то время как довольно новый на сцене - предлагает наиболее привлекательные точки продажи для поколения исполнения. Достойная скорость обновления, и автор имеет глубокое знание внутренних элементов байтового кода Java.
Ответ 2
Если вам нужно сделать прокси, взгляните на commons-proxy - он использует как CGLIB, так и Javassit.
Ответ 3
Я предпочитаю raw ASM, который, как мне кажется, используется cglib. Это низкий уровень, но документация блестящая, и как только вы привыкнете к ней, вы будете летать.
Чтобы ответить на второй вопрос, вы должны использовать генерацию кода, когда ваши рефлексии и динамические прокси начинают чувствовать себя немного мощеными, и вам нужно твердое решение. Раньше я даже добавлял шаг генерации кода в процесс сборки в Eclipse, эффективно предоставляя мне время компиляции всего и всего.
Ответ 4
Я думаю, что больше смысла использовать Javassist вместо cglib. Например. javasist отлично работает с подписанными банками, в отличие от cglib. Кроме того, такой грандиозный, как проект Hibernate решил прекратить использовать cglib в пользу Javassist.
Ответ 5
Proxetta - это еще один менее известный Java-прокси-сервер.
Ответ 6
CGLIB был разработан и внедрен более десяти лет назад в эпоху АОП и ОРМ. В настоящее время я не вижу причин использовать его, и я больше не поддерживаю эту библиотеку (кроме исправлений ошибок для моих устаревших приложений). На самом деле все случаи использования CGLIB, которые я когда-либо видел, - это анти-шаблоны в современном программировании. Это должно быть тривиально реализовать одну и ту же функциональность с помощью любого языка сценариев JVM, например. groovy.