Вопрос

Просто из любопытства, существуют ли какие-либо (стабильные) проекты с открытым исходным кодом для генерации java-кода во время выполнения, отличные от cglib?И почему я должен ими пользоваться?

Это было полезно?

Решение

ASM

CGLIB и почти все другие библиотеки построены поверх ASM, который сам по себе работает на очень низком уровне.Это ограничитель показа для большинства людей, поскольку вы должны понимать байт-код и немного Совместные предприятия чтобы использовать его должным образом.Но осваивать ASM, безусловно, очень интересно.Однако обратите внимание, что, хотя существует великий Руководство ASM 4, в какой-то части API документация javadoc может быть очень краткой, если она вообще присутствует, но она совершенствуется.Он полностью соответствует версиям JVM для поддержки новых функций.

Однако, если вам нужен полный контроль, ASM - это ваше оружие выбора.

Этот проект регулярно обновляется ;на момент этой правки версия 5.0.4 была выпущена 15 мая 2015 года.

Байтовый Приятель

Byte Buddy - довольно новая библиотека, но предоставляет любую функциональность, которую предоставляют CGLIB или Javassist, и многое другое.Byte Buddy может быть полностью настроен вплоть до уровня байтового кода и поставляется с выразительным языком, специфичным для конкретной предметной области, что позволяет создавать очень читаемый код.

  • Он поддерживает все версии байт-кода JVM, включая семантические изменения Java 8 некоторых кодов операций относительно методов по умолчанию.
  • ByteBuddy, похоже, не страдает от недостатков, присущих другим библиотекам
  • Высокая степень настройки
  • Довольно быстро (эталонный показатель код)
  • Введите безопасный беглый API
  • Введите безопасные обратные вызовы

    Советы Javassist или пользовательский инструментальный код основаны на коде в обычном String таким образом, проверка типов и отладка невозможны в этом коде, в то время как ByteBuddy позволяет писать их на чистой Java, следовательно, принудительно проверяет типы и разрешает отладку.

  • Управляемый аннотациями (гибкий)

    Пользовательские обратные вызовы могут быть сконфигурированы с аннотациями, позволяющими получать требуемые параметры в обратном вызове.

  • Доступен в качестве агента

    Nifty agent builder позволяет использовать ByteBuddy как чистый агент или как присоединяющий агент.Это позволяет использовать различные виды

  • Очень хорошо документировано
  • Множество примеров
  • Чистый код, ~ 94% тестового покрытия
  • Поддержка Android DEX

Возможно, основным недостатком является то, что API немного многословен для новичка, но он разработан как opt-in API в форме DSL поколения прокси ;здесь нет никакой магии или сомнительных настроек по умолчанию.При манипулировании байт-кодом это, вероятно, самый безопасный и разумный выбор.Кроме того, с несколькими примерами и большим руководством это не является реальной проблемой.

В октябре 2015 года эти проекты получили Приз "Выбор оракула Дюка".В это время дело как раз дошло до 1.0.0 веха, что является настоящим достижением.

Обратите внимание , что заменил CGLIB по байтам Приятель в версии 2.1.0.

Джавасист

Javadoc Javassist намного лучше, чем у CGLIB.API разработки классов в порядке, но Javassist тоже не идеален.В частности, ProxyFactory который является эквивалентом CGLIB Enhancer я тоже страдаю от некоторых недостатков, просто чтобы перечислить некоторые :

  • Метод моста поддерживается не полностью (т.е. тот, который генерируется для ковариантных возвращаемых типов)
  • ClassloaderProvider вместо этого это статическое поле, тогда оно применяется ко всем экземплярам внутри одного и того же classloader
  • Пользовательское присвоение имен могло бы приветствоваться (с проверками подписанных банок).
  • Точки расширения нет, и почти все представляющие интерес методы являются частными, что является громоздким, если мы хотим изменить какое-то поведение
  • Хотя Javassist предлагает поддержку атрибутов аннотаций в классах, они не поддерживаются в ProxyFactory.

С аспектно-ориентированной стороны, можно внедрить код в прокси, но этот подход в Javassist ограничен и немного подвержен ошибкам :

  • аспектный код написан в простой строке Java, которая является скомпилированный в кодах операций
  • нет проверки типа
  • никаких дженериков
  • нет лямбды
  • нет автоматической (не) блокировки

Также Javassist признан более медленным, чем Cglib.В основном это связано с его подходом к чтению файлов классов вместо чтения загруженных классов, таких как CGLIB.И тот реализация честно говоря, сам по себе труден для чтения ;если кому-то требуется внести изменения в код Javassist, есть много шансов что-то сломать.

Javassist также пострадали от бездействия, их переезд в github около 2013 года похоже, он оказался полезным, поскольку показывает регулярные коммиты и запросы на получение данных от сообщества.

Эти ограничения по-прежнему действуют в версии 3.17.1.Версия была обновлена до версии 3.20.0, но, похоже, у Javassist все еще могут быть проблемы с поддержкой Java 8.

JiteScript - файл

JiteScript действительно кажется новой частью красиво оформленного DSL для ASM, он основан на последней версии ASM (4.0).Код выглядит чистым.

Но проект все еще находится в раннем возрасте, поэтому API / поведение могут измениться, плюс документация ужасна.И обновления редки, если не заброшены.

Проксетта

Это довольно новый инструмент, но он предлагает, безусловно, лучшее человек API.Это позволяет использовать различные типы прокси, такие как прокси подкласса (подход cglib), переплетение или делегирование.

Хотя этот метод довольно редок, никакой информации о том, хорошо ли он работает, не существует.При работе с байт-кодом приходится иметь дело со многими угловыми случаями.

Аспект j

AspectJ - это очень мощный инструмент для аспектно-ориентированное программирование (только).AspectJ манипулирует байтовым кодом для достижения своих целей таким образом, чтобы вы могли достичь с его помощью своих целей.Однако это требует манипуляций во время компиляции;весеннее предложение переплетается во время загрузки через агента начиная с версии 2.5, 4.1.x.

CGLIB

Несколько слов о CGLIB, который был обновлен с тех пор, как был задан этот вопрос.

CGLIB работает довольно быстро, это одна из главных причин, почему он все еще существует, наряду с тем фактом, что CGLIB работал почти лучше, чем любые альтернативы до сих пор (2014-2015).

Вообще говоря, библиотеки, которые позволяют переписывать классы во время выполнения, должны избегать загрузки каких-либо типов до перезаписи соответствующего класса.Следовательно, они не могут использовать Java reflection API, который требует, чтобы был загружен любой тип, используемый в reflection.Вместо этого они должны читать файлы классов через IO (что снижает производительность).Это делает, например, Javassist или Proxetta значительно медленнее, чем Cglib, который просто считывает методы через reflection API и переопределяет их.

Однако CGLIB больше не находится в стадии активной разработки.Были недавние выпуски, но многие сочли эти изменения незначительными, и большинство людей никогда не обновлялись до версии 3, поскольку CGLIB представил некоторые серьезные ошибки в последних выпусках то, что на самом деле не вызывало доверия. Версия 3.1 исправила многие проблемы версии 3.0 (начиная с версии 4.0.3 переупаковки Spring Framework версия 3.1).

Кроме того, исходный код CGLIB имеет довольно низкое качество таким образом, мы не видим новых разработчиков, присоединяющихся к проекту CGLIB.Чтобы получить представление об активности CGLIB, смотрите Их список рассылки.

Обратите внимание, что после предложение в списке рассылки guice, CGLIB теперь доступен на гитхаб чтобы сообщество могло лучше помогать проекту, он, похоже, работает (несколько коммитов и запросов на извлечение, ci, обновленный maven), но большинство проблем все еще остаются.

В настоящее время ведется работа над версией 3.2.0, и они сосредотачивают усилия на Java 8, но пока пользователям, которые хотят получить поддержку java 8, приходится использовать хитрости во время сборки.Но прогресс идет очень медленно.

А CGLIB, как известно, по-прежнему страдает от постоянной утечки памяти.Но другие проекты, возможно, не проходили боевых испытаний в течение стольких лет.

Обработка аннотаций во время компиляции

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

Это началось с 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 год, Байтовый Приятельпока довольно новичок на сцене — предлагайте самые привлекательные продажа баллы за генерацию во время выполнения.Приличная частота обновления, и автор обладает глубокими знаниями внутренних компонентов байт-кода Java.

Другие советы

Явасист.

Если вам нужно сделать прокси, взгляните на Commons-прокси - он использует как CGLIB, так и Javassit.

я предпочитаю сырой КАК М, который, я полагаю, в любом случае используется cglib.Это низкий уровень, но документация блестящий, и как только вы к этому привыкнете, вы начнете летать.

Чтобы ответить на второй вопрос, вам следует использовать генерацию кода, когда ваше отражение и динамические прокси начинают казаться немного запутанными и вам нужно надежное решение.Раньше я даже добавлял этап генерации кода в процесс сборки в Eclipse, что эффективно давало мне отчеты обо всем и вся во время компиляции.

Я думаю, что разумнее использовать Явасист вместо cglib.Например.javasist прекрасно работает с подписанными jar-файлами в отличие от cglib.Кроме того, такой грандиозный проект, как Hibernate решил отказаться от использования cglib в пользу Javassist.

CGLIB был разработан и реализован более десяти лет назад в эпоху AOP и ORM.В настоящее время я не вижу причин использовать ее и больше не поддерживаю эту библиотеку (за исключением исправления ошибок в моих устаревших приложениях).На самом деле все случаи использования CGLIB, которые я когда-либо видел, являются антипаттернами в современном программировании.Должно быть тривиально реализовать ту же функциональность с помощью любого языка сценариев JVM, например.классный.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top