Byte Buddy - это библиотека генерации кода и манипуляции для создания и изменения классов Java во время выполнения приложения Java и без помощи компилятора. Помимо утилит генерации кода, которые поставляются с библиотекой классов Java, Byte Buddy позволяет создавать произвольные классы и не ограничивается внедрением интерфейсов для создания прокси -серверов времени выполнения. Кроме того, Byte Buddy предлагает удобный API для изменения классов вручную, используя агент Java или во время сборки.
Для использования байтового приятеля не требуется понимание байтового кода Java или формата файла класса. Напротив, API Byte Buddy нацелен на код, который является кратким и простым для всех для всех. Тем не менее, байтовый приятель остается полностью настраиваемым вплоть до возможности определения пользовательского байтового кода. Кроме того, API был разработан, чтобы быть максимально неинтрузивным, насколько это возможно, и в результате Byte Buddy не оставляет никаких трассов в классах, которые были созданы им. По этой причине сгенерированные классы могут существовать, не требуя байтового приятеля на пути класса. Из -за этой функции талисман байтового Бадди был выбран в качестве призрака.
Byte Buddy написан на Java 5, но поддерживает поколение классов для любой версии Java. Byte Buddy является легкой библиотекой и зависит только от API посетителя библиотеки анализатора байтового кода Java ASM, которая сама не требует дальнейших зависимостей.
На первый взгляд, генерация кода времени выполнения может показаться какой -то черной магией, которой следует избегать, и только немногие разработчики пишут приложения, которые явно генерируют код во время их выполнения. Тем не менее, эта картина меняется при создании библиотек, которые должны взаимодействовать с произвольным кодом и типами, которые неизвестны во время компиляции. В этом контексте библиотечный реализатор часто должен выбирать между требованием пользователя для реализации библиотечных предпринятых интерфейсов или генерировать код во время выполнения, когда типы пользователя становятся сначала известны библиотеке. Многие известные библиотеки, такие как, например, Spring или Hibernate, выбирают последний подход, который популярен среди их пользователей в рамках срока использования простых старых объектов Java . В результате генерация кода стала повсеместной концепцией в пространстве Java. Byte Buddy - это попытка внедрить внедрение создания времени выполнения типов Java, чтобы обеспечить лучший набор инструментов для тех, кто полагается на генерацию кода.
В октябре 2015 года Байт Бадди отличился наградой «Выбор герцога» Oracle. Награда ценит байтового приятеля за « огромное количество инноваций в технологии Java ». Мы чувствуем себя очень честью за то, что получили эту награду и хотим поблагодарить всех пользователей и всех, кто помог сделать байтового приятеля таким успехом. Мы действительно ценим это!
Byte Buddy предлагает отличную производительность на качество производства. Он стабилен и используется выдающимися фреймворками и инструментами, такими как Mockito, Hibernate, Jackson, система Bazel Build и многие другие. Byte Buddy также используется большим количеством коммерческих продуктов для отличного результата. В настоящее время он загружается более 75 миллионов раз в год.
Говорить Hello World с байтовым приятелем настолько прост, насколько это возможно. Любое создание класса Java начинается с экземпляра класса ByteBuddy
, который представляет собой конфигурацию для создания новых типов:
Class <?> dynamicType = new ByteBuddy ()
. subclass ( Object . class )
. method ( ElementMatchers . named ( "toString" ))
. intercept ( FixedValue . value ( "Hello World!" ))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat ( dynamicType . newInstance (). toString (), is ( "Hello World!" ));
Конфигурация ByteBuddy
по умолчанию, которая используется в приведенном выше примере, создает класс Java в новой версии формата файла класса, которая понимается с помощью виртуальной машины Java. Как мы надеемся, очевидно из примера, созданный тип расширит класс Object
и переопределяет его метод toString
, который должен вернуть фиксированное значение Hello World!
Полем Метод, который должен быть переопределен, идентифицируется так называемым ElementMatcher
. В приведенном выше примере используется предопределенный сочетание элементов named(String)
, который идентифицирует методы по их точным именам. Byte Buddy поставляется с многочисленными предопределенными и хорошо проверенными маттерами, которые собираются в классе ElementMatchers
и которые могут быть легко составлены. Однако создание пользовательских маттера так же просто, как реализация (функционального) интерфейса ElementMatcher
.
Для реализации метода toString
класс FixedValue
определяет постоянное возвратное значение для переопределенного метода. Определение постоянного значения является только одним примером многих перехватчиков методов, которые поставляются с байтовым приятелем. Реализуя интерфейс Implementation
, метод, однако, может быть определен по специальному байтовому коду.
Наконец, описанный класс Java создается, а затем загружается в виртуальную машину Java. Для этой цели требуется загрузчик целевого класса. В конце концов, мы можем убедить себя в результате, вызывая метод toString
в экземпляре созданного класса и обнаружив возвращаемое значение для представления постоянного значения, которое мы ожидали.
Конечно, пример Hello World - это слишком простой вариант использования для оценки качества библиотеки генерации кодов. На самом деле пользователь такой библиотеки хочет выполнить более сложные манипуляции, например, введя крючки в путь выполнения программы Java. Используя байтового приятеля, это, однако, одинаково просто. Следующий пример дает вкус того, как можно перехватить вызовы методов.
Byte Buddy выражает динамически определенные реализации методов по случаям интерфейса Implementation
. В предыдущем примере FixedValue
, который реализует этот интерфейс, уже был продемонстрирован. Реализуя этот интерфейс, пользователь байтового приятеля может перейти к длине определения пользовательского байтового кода для метода. Обычно, однако, легче использовать предопределенные реализации Byte Buddy, такие как MethodDelegation
, что позволяет реализовать любой метод в простой Java. Использование этой реализации является простым, поскольку она работает путем делегирования потока управления любому POJO. В качестве примера такого Pojo Byte Buddy может, например, перенаправить призыв к единственному методу следующего класса:
public class GreetingInterceptor {
public Object greet ( Object argument ) {
return "Hello from " + argument ;
}
}
Обратите внимание, что приведенный выше GreetingInterceptor
не зависит от какого -либо типа байтового приятеля. Это хорошие новости, потому что ни один из классов, которые генерирует байт -приятель, не требует байтового приятеля на пути к классу! Учитывая вышеупомянутый GreetingInterceptor
, мы можем использовать байтового приятеля для реализации интерфейса java 8 java.util.function.Function
и его метода Abstract apply
:
Class <? extends java . util . function . Function > dynamicType = new ByteBuddy ()
. subclass ( java . util . function . Function . class )
. method ( ElementMatchers . named ( "apply" ))
. intercept ( MethodDelegation . to ( new GreetingInterceptor ()))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat (( String ) dynamicType . newInstance (). apply ( "Byte Buddy" ), is ( "Hello from Byte Buddy" ));
Выполняя приведенный выше код, Byte Buddy реализует интерфейс Function
Java и реализует метод apply
в качестве делегирования к экземпляру GreetingInterceptor
Pojo, который мы определяли ранее. Теперь, каждый раз, когда называется метод Function::apply
, поток управления отправляется на GreetingInterceptor::greet
, а возвращаемое значение последнего возвращается из метода интерфейса.
Перехватыватели могут быть определены, чтобы принять с большим количеством общих входов и выходов путем аннотирования параметров перехвата. Когда байтовый приятель обнаруживает аннотацию, библиотека вводит зависимость, которую требует параметр перехвата. Примером для более общего перехватчика является следующий класс:
public class GeneralInterceptor {
@ RuntimeType
public Object intercept ( @ AllArguments Object [] allArguments ,
@ Origin Method method ) {
// intercept any method of any signature
}
}
С приведенным выше перехватчиком любой перехваченный метод может быть сопоставлен и обработан. Например, при сопоставлении Function::apply
аргументы метода будут переданы как единственный элемент массива. Кроме того, ссылка на Method
на Fuction::apply
будет передаваться в качестве второго аргумента перехвата из -за аннотации @Origin
. Объявив аннотацию @RuntimeType
на методе, Байт Бадди наконец -то подгоняет возвращаемое значение до возвращаемого значения перехваченного метода, если это необходимо. При этом Byte Buddy также применяет автоматическое бокс и распаковку.
Помимо аннотаций, которые уже были упомянуты, существует множество других предопределенных аннотаций. Например, при использовании аннотации @SuperCall
на Runnable
или Callable
типе Byte Buddy вводит доверенные экземпляры, которые позволяют вызов неабстрактного супер-метода, если такой метод существует. И даже если Byte Buddy не покрывает вариант использования, Byte Buddy предлагает механизм расширения для определения пользовательских аннотаций.
Вы можете ожидать, что использование этих аннотаций связывает ваш код с байтовым приятелем. Тем не менее, Java игнорирует аннотации, если они не видны классовым загрузчикам. Таким образом, сгенерированный код все еще может существовать без байтового приятеля! Вы можете найти больше информации о MethodDelegation
и по всем его предопределенным аннотациям в его Javadoc и в учебном пособии Byte Buddy.
Byte Buddy не ограничивается созданием подклассов, но также способен пересмотреть существующий код. Для этого Byte Buddy предлагает удобный API для определения так называемых агентов Java. Агенты Java - это простые старые программы Java, которые можно использовать для изменения кода существующего приложения Java во время выполнения. Например, мы можем использовать байтового приятеля, чтобы изменить методы для печати времени их исполнения. Для этого мы сначала определяем перехватчик, похожий на перехватчиков в предыдущих примерах:
public class TimingInterceptor {
@ RuntimeType
public static Object intercept ( @ Origin Method method ,
@ SuperCall Callable <?> callable ) {
long start = System . currentTimeMillis ();
try {
return callable . call ();
} finally {
System . out . println ( method + " took " + ( System . currentTimeMillis () - start ));
}
}
}
Используя агент Java, теперь мы можем применить этот перехватчик ко всем типам, которые соответствуют ElementMatcher
для TypeDescription
. Для примера мы решили добавить вышеупомянутый перехватчик ко всем типам с именем, которое заканчивается Timed
. Это делается ради простоты, тогда как аннотация, вероятно, была бы более подходящей альтернативой, чтобы отметить такие классы для производственного агента. Использование API API AgentBuilder
's Byte Buddy, создание агента Java так же просто, как определение следующего класса агента:
public class TimerAgent {
public static void premain ( String arguments ,
Instrumentation instrumentation ) {
new AgentBuilder . Default ()
. type ( ElementMatchers . nameEndsWith ( "Timed" ))
. transform (( builder , type , classLoader , module , protectionDomain ) ->
builder . method ( ElementMatchers . any ())
. intercept ( MethodDelegation . to ( TimingInterceptor . class ))
). installOn ( instrumentation );
}
}
Подобно main
методу Java, метод premain
является точкой входа для любого агента Java, из которого мы применяем переопределение. В качестве одного аргумента, агент Java получает экземпляр интерфейса Instrumentation
, который позволяет Byte Buddy зацепиться в стандартный API JVM для переосмысления класса выполнения.
Эта программа упакована вместе с манифестным файлом с атрибутом Premain-Class
указывающим на TimerAgent
. Полученный файл JAR теперь может быть добавлен в любое приложение Java, установив -javaagent:timingagent.jar
похожий на добавление банки к пути класса. С активным агентом все классы, заканчивающиеся по Timed
, теперь печатают время выполнения в консоли.
Byte Buddy также способен применять так называемые вложения времени выполнения путем отключения изменений формата файла класса и используя инструмент Advice
. Пожалуйста, обратитесь к Javadoc Advice
и классу AgentBuilder
для получения дополнительной информации. Byte Buddy также предлагает явное изменение классов Java через экземпляр ByteBuddy
или с помощью плагинов Byte Buddy Maven и Gradle .
Byte Buddy - это всеобъемлющая библиотека, и мы поцарапали только поверхность возможностей Байтового Бадди. Тем не менее, Byte Buddy стремится быть простым в использовании, предоставляя специфический для домена язык для создания классов. Большая часть генерации кода выполнения может быть сделана путем написания читаемого кода и без каких -либо знаний о формате файла класса Java. Если вы хотите узнать больше о Byte Buddy, вы можете найти такой учебник на веб -странице Byte Buddy (есть также китайский перевод).
Кроме того, Byte Buddy поставляется с подробной документацией в коде и обширном тестовом покрытии, которые также могут служить примером кода. Наконец, вы можете найти актуальный список статей и презентаций на байтовом приятеле в вики. При использовании байтового приятеля также обязательно прочитайте следующую информацию о поддержании зависимости проекта.
Использование байтового приятеля бесплатно и не требует покупки лицензии. Чтобы получить максимальную отдачу от библиотеки или обеспечить легкий старт, однако можно приобрести обучение, часы разработки или планы поддержки. Ставки зависят от объема и продолжительности взаимодействия. Пожалуйста, свяжитесь с [email protected] для получения дополнительной информации.
Byte Buddy указан на Tidelift. Если вы не используете Byte Buddy до такой степени, что вы хотите приобрести явную поддержку и хотите поддержать сообщество с открытым исходным кодом в целом, пожалуйста, рассмотрите подписку.
Вы можете поддержать мою работу через спонсоров GitHub. Обратите внимание, что этот вариант предназначен только для коммерческих актеров, которые ищут простой платежный канал, и которые не ожидают поддержки взамен. Поддержка через спонсоры GitHub невозможно поддерживать соответствие НДС. Пожалуйста, обратитесь к соглашению о прямой поддержке вместо этого.
Общие вопросы могут быть заданы по переполнению стека или в списке рассылки байтового приятеля, который также служат архивом для вопросов. Конечно, отчеты об ошибках будут рассмотрены также за пределами коммерческого плана. Для проектов с открытым исходным кодом иногда можно получить расширенную помощь для использования байтового приятеля.
Byte Buddy написан на вершине ASM, зрелой и хорошо проверяемой библиотеке для чтения и письма, скомпилированных классов Java. Чтобы разрешить манипуляции с продвинутым типом, байтовый приятель намеренно выявляет API ASM своим пользователям. Конечно, прямое использование ASM остается полностью необязательным, и большинство пользователей, скорее всего, никогда не потребуют этого. Этот выбор был сделан таким образом, чтобы пользователь байтового приятеля не ограничивался функцией более высокого уровня, но мог реализовать пользовательские реализации без суеты, когда это необходимо.
ASM ранее изменил свой публичный API, но добавил механизм совместимости API, начиная с версии 4 библиотеки. Чтобы избежать конфликтов версий с такими более старыми версиями, байтовый приятель перетаскивает зависимость ASM в свое пространство имен. Если вы хотите использовать ASM напрямую, byte-buddy-dep
артефакт предлагает версию байтового приятеля с явной зависимостью от ASM. При этом вы должны переупаковать как Byte Buddy, так и ASM в ваше пространство имен, чтобы избежать конфликтов версий.
Обратите внимание на политику безопасности этого проекта.
Byte Buddy поддерживает выполнение во всех версиях JVM из пятой версии и далее в одной банке. Это сделано для облегчения разработки агентов Java, которые часто требуются для поддержки старых или неизвестных приложений, которые не обновляются активно. Чтобы разрешить это, одновременно поддерживая современную Java и такие функции, как CDS или проверка класса с помощью кадров карты стека, основные банки для байтовых судов в качестве многоразрешенных банок, которые содержат файлы классов в пятой версии и восьми. В результате размер банки байтового приятеля выше, как и следовало ожидать. Размер файла JAR обычно не является проблемой, так как большинство классов байтового приятеля никогда не будут загружены. Тем не менее, размер файла может быть проблемой при распределении агентов Java. Поскольку агенты уже должны быть связаны как одна банка, поэтому рекомендуется удалить либо основную версию Java Five, либо многоразрешенную версию Java Eight из содержащихся классов, чтобы уменьшить эту проблему. Это поддерживается большинством плагинов сборки для этой цели, такими как плагин Maven Shade.
Byte Buddy имеет лицензию по лицензии Apache лицензии на либеральную и благоприятную для бизнеса, версия 2.0 и свободно доступен на Github. Кроме того, байто- распределительные пучки ASM, которая выпускается по лицензии BSD с 3 пунктами.
Byte Buddy Binary публикуются в репозиториях Maven Central и On Jcenter. Подписи артефактов могут быть подтверждены против этого открытого ключа PGP, начиная с байтового приятеля 1.10.3. Старые версии могут быть подтверждены против этого более старого и более слабого сертификата.
Проект построен с использованием Maven. Из вашей оболочки, клонирование и строительство проекта пойдет на что -то вроде этого:
git clone https://github.com/raphw/byte-buddy.git
cd byte-buddy
mvn package
В этих командах байтовый приятель клонирован от GitHub и построен на вашей машине. Дальнейшие варианты сборки перечислены в корневом файле POM. Byte Buddy может быть построен с любым JDK, по крайней мере, версией 6. Однако рекомендуется использовать JDK, по крайней мере, версию 8, поскольку сборки для версии 6 и 7 требуют использования незашифрованного HTTP. Его поддержка предназначена только для выполнения тестов против этой версии JDK и может подвергнуть вас атакам человека в среднем уровне. Поэтому эти сборки следует избегать. Byte Buddy в настоящее время проверяется на версии 6 и выше JDK на серверах CI.
Пожалуйста, используйте трекер выпуска GitHub для отчетности об ошибках. При совершении кода, пожалуйста, предоставьте тестовые примеры, которые доказывают функциональность ваших функций или которые демонстрируют исправление ошибки. Кроме того, убедитесь, что вы не ломаете никаких существующих тестовых случаев. Если возможно, пожалуйста, найдите время, чтобы написать некоторую документацию. Для запросов на функции или общих отзывов вы также можете использовать The Tracker или связаться с нами в нашем списке рассылки.
Работа над байтовым приятелем также возможна благодаря ряду сторонников, которые уделяют регулярные ресурсы и внимание к проекту. Пожалуйста, не торопитесь, чтобы взглянуть на этих сторонников и их предложения.