Переписывание вызовов методов в скомпилированных классах Java
Я хочу заменить вызовы данного класса вызовами другого класса в теле метода при анализе файлов скомпилированного класса...
или, другими словами, существует ли метод определения использования данного класса в методе и замены только этой части метода с помощью чего-то вроде javaassist.
например.. если бы у меня была скомпилированная версия
class A { public int m() { int i = 2; B.multiply(i,i); return i; } }
Есть ли способ обнаружения использования B, а затем изменение кода для выполнения
class A { public int m() { int i = 2; C.divide(i,i); return i; } }
Я знаю, что альтернативой будет написать синтаксический анализатор для поиска исходных файлов для использования, но я бы предпочел более элегантное решение, такое как использование отражения для генерации новых скомпилированных файлов классов.
Какие-нибудь мысли?
5 ответов
Как говорит @djna, можно изменить файлы байт-кода перед их загрузкой, но вы, вероятно, не хотите делать это:
- Код, который выполняет модификацию кода, вероятно, будет сложным и сложным в обслуживании.
- Код, который был изменен, вероятно, будет трудно отлаживать. Для начала отладчик исходного уровня покажет вам исходный код, который больше не соответствует коду, который вы на самом деле редактируете.
Перезапись байт-кода полезна в некоторых случаях. Например, реализации JDO используют переписывание байт-кода для замены выборок членов объекта вызовами в постоянные библиотеки. Однако, если у вас есть доступ к исходному коду для этих файлов, вы получите лучшее (то есть более удобное в обслуживании) решение, предварительно обработав (или сгенерировав) исходный код.
РЕДАКТИРОВАТЬ: и AOP или Groovy тоже звучат как жизнеспособные альтернативы, в зависимости от степени переписывания, которую вы ожидаете сделать.
Недавно я просмотрел несколько библиотек для чтения файлов классов Java. BCEL был самым быстрым, имел наименьшее количество зависимостей, компилировался из коробки и имел восхитительно простой API. Я предпочел BCEL, а не ASM, потому что у ASM больше зависимостей (хотя API, как считается, проще).
AspectJ, как упоминалось ранее, является еще одним жизнеспособным вариантом.
BCEL действительно прост. Вы можете получить список методов в трех строках кода:
ClassParser cp = new ClassParser( "A.class" );
JavaClass jc = cp.parse();
Method[] m = jc.getMethods();
Существуют и другие возможности API для дальнейшего самоанализа, включая, я полагаю, способы получения инструкций в методе. Однако это решение, вероятно, будет более трудоемким, чем AspectJ.
Другая возможность - изменить сами методы умножения или деления, а не пытаться изменить все экземпляры кода, который вызывает операцию. Это было бы легче сделать с BCEL (или ASM).
Если вы не возражаете против использования Groovy, вы можете перехватить вызов B.multiply
и заменить его на C.divide
, Вы можете найти пример здесь.
Указан формат байт-кода для скомпилированной Java, и существуют продукты, которые им манипулируют.
Эта библиотека, кажется, имеет необходимые вам возможности. Я понятия не имею, насколько легко сделать эти преобразования надежно.
Гораздо проще выполнять эти операции заранее, когда исполняемый файл на диске изменяется перед запуском приложения. Манипулирование кодом в памяти во время выполнения даже более подвержено ошибкам, чем манипулирование кодом в памяти в C/C++. Зачем тебе это нужно?