2010-11-09 3 views
11

по теме: Is there a way to obtain the bytecode for a class at runtime?Java: Получение байт-код класса во время выполнения в пределах одной виртуальной машины Java

Я добавляю долговечность Clojure, и я, наконец, в точке, где я готов добавить функции. В Clojure функции байт компилируются в классы с помощью методов invoke (среди прочих). Таким образом, функции являются первоклассными. Чтобы сделать эти прочные, мне нужно сериализовать и десериализовать эти классы. Как получить байт-код для класса без доступа к файлу .class?

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

Недостаточно использовать Serializable для установки и получения объекта Class. При десериализации мне нужно загрузить класс, а после последующих экземпляров VM может возникнуть конфликт имен классов. Мне нужно изменить байт-код, чтобы переименовать класс в нечто уникальное при десериализации/времени загрузки класса.

+0

Я никоим образом не специалист по этой теме, но, возможно, стоит попытаться сохранить функции * определения *, а не базовый байт-код. вы можете просто перекомпилировать функции для байт-кода при их повторном загрузке. – mikera

ответ

4

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

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

+0

p.s. Я думаю, что агенты работают в той же виртуальной машине, что и основная программа, поэтому они могут работать на вас. –

+0

Javassist уже реализует эту схему и предлагает несколько удобный доступ к рассматриваемому байткоду. BCEL может также сделать что-то подобное. – Blaisorblade

+0

Минимальная реализация довольно тривиальная: https://gist.github.com/Felk/ed4375d27c755e21d0e6893847286d93 (я знаю, что это сообщение 7 лет) – Felk

3

Если вы не используете код с помощью хитрого загрузчика, вы должны быть в состоянии сделать что-то вроде этого:

Class<?> clazz = .... 
String className = clazz.getCanonicalName(); // e.g. "foo.Bar" 
String resourceName = ... // map className to a resource name; e.g. "/foo/Bar.class" 
InputStream is = clazz.getClassLoader.getResourceAsStream(resourceName); 

Это дает вам ручку на содержимом файла «.class» ... если его можно найти.

Предостережения. Некоторые загрузчики классы могли бы:

  • не позволить, чтобы открыть «.class» ресурсы на всех,
  • даст вам зашифрованный поток байт-коду, или
  • дать вам байткоды, которые ни то, что в настоящее время работает, из-за к некоторому преобразованию «на лету», выполняемому загрузчиком классов.

Если этот подход не работает, вы почти не используете опции, поскольку JVM не предоставляет способ доступа к фактическим байт-кодам, которые были загружены.

+1

Увы, это работает только в том случае, если .class записывается на диск в качестве ресурса в первую очередь. Clojure этого не делает. – alyssackwan

+0

У этого также есть проблемы, когда класс определен внутри другого. Например: 'class Foo {class Bar {}}' должен стать «/Foo$Bar.class» – iliis

+1

'clazz.getResourceAsStream ('/' + clazz.getName(). Replace ('.', '/') + «.class») 'работает с внутренними классами и даже с классами начальной загрузки, имеющими« null »ClassLoader. – Holger

1

Для этого также можно использовать Java Instrumentation API. Вы получаете доступ к байтам файла классов перед вызовом defineClass. Вы тоже можете их изменить!