Добавление кода в класс Java w/Instrumentation: ASM или BCEL?
Я пишу игровой движок/библиотеку, в которой у меня есть класс диспетчера событий, который отправляет события, вызывая методы прослушивания "зарегистрированных" классов обработчиков событий. Можно зарегистрировать обработчик/прослушиватель событий с диспетчером событий, вызвав соответствующий метод диспетчера.
Это, очевидно, приводит к некоторому шаблонному коду для регистрации каждого обработчика событий (а также другие аспекты моего движка имеют аналогичный код на основе bolierplate), поэтому мне было интересно - как просто использовать Instrumentation для добавления всего необходимого кода во время загрузки класс обработчика событий, так что при кодировании не требуется явная регистрация с диспетчером событий. При запуске программы автоматически добавляется вызов метода регистрационного диспетчера.
Я понимаю, что для использования Instrumentation следует использовать API-интерфейс байтового кода. Я знаю два - ASM и BCEL. Какой из них я должен использовать? Очевидно, это несколько простая задача, которую я пытаюсь сделать, поэтому я хочу тот, который легче изучить и лучше документировать.
EDIT: Вот пример.
Класс обработчика исходных событий:
@Handler //indicates this this class should be transformed
public class MouseEventHandler implements EventHandler<MouseEvent>
{
//hidden default constructor
public void handleEvent(MouseEvent event)
{ ... }
}
После преобразования:
@Handler
public class MouseEventHandler implements EventHandler<MouseEvent>
{
public MouseEventHandler()
{
//add this line of code to default constructor
Game.getEventDispatcher().addEventHandler(this);
}
public void handleEvent(MouseEvent event)
{ ... }
}
Ответы
Ответ 1
Я бы рассмотрел другие варианты, прежде чем переходить на байт-код манипуляции.
Ответ 2
Добавление логики к нескольким классам может быть скучным, но если у вас нет тысяч обработчиков, то, как я пойду. Держите его простым.
Тем не менее,
Game.registerHandler( this );
будет более объектно-ориентированным.
Альтернативой добавлению логики в каждый класс является представление factory, который отвечает за создание экземпляров обработчиков.
HandlerFactory.createMouseHandler();
И метод createMouseHandler
содержит что-то вроде
Handler mh = new MousheHandler();
registerHandler(mh);
return mh;
Если вы не хотите ни одного из этих параметров, я бы рассмотрел либо структуру фрейма (возможно, AspectJ), либо контейнер для инверсии управления (возможно, Spring IoC). Аспекты позволяют вам комментировать ваш источник и "переплетать" код в выбранных местах. Контейнер IoC позволяет вам контролировать жизненный цикл объекта (например, экземпляр). Оба используют инструментарий байт-кода за сценой.
Но если вы хотите сделать инструментарий самостоятельно, я могу сравнить только Javassist и ASM, которые я использовал лично.
ASM является низкоуровневым и работает на уровне java-байт-кода. Вы должны быть знакомы с этим. Структура очень хорошо разработана, руководство отлично, и это отличная библиотека. Одна из сторон может усложнить замену шаблонов байт-кода, поскольку для этого требуется так называемое преобразование "stateful". С другой стороны, вы полностью контролируете байт-код.
Javassist более высокий уровень. Вы не работаете на необработанном уровне байт-кода, немного более высоком уровне, например. поля читать/писать, отправлять сообщения, конструкторы. Кроме того, он позволяет указывать изменения с использованием регулярного синтаксиса java, который затем компилируется каркасом. API немного запутан, потому что проект рос с годами. Существует документация о структуре, но не так хорошо централизована, как с ASM.