2016-07-29 6 views
4
  • У меня есть класс А продолжается Б.
  • Я создал CustomClassLoader расширяет ClassLoader использовать defineClass(className, byte[], offset, length).
  • Я инстанцировал new CustomClassLoader(Thread.currentThread().getContextClassLoader()). Итак, родительский элемент моего CustomClassLoader является ClassLoader из текущего потока.
  • Я модифицировал класс B с использованием рамки ASM. Я написал свой модифицированный класс в файле .class и использую декомпилятор, чтобы убедиться, что он работает. И это работает.
  • Я добавил измененный класс B в свой CustomClassLoader
  • Я установил Thread.currentThread().setContextClassLoader() с помощью своего CustomClassLoader.
  • Я загружаю A, используя Class.forName(String, true, the CustomClassLoader).
  • Но загруженный класс B, кажется, является оригинальным классом.

Что я не так понял? Если вам нужна дополнительная информация, подробная тема на моем GitHub.Как загрузить модифицированный суперкласс в Java?

ответ

2

Java classloaders Первый ищет родительский загрузчик классов перед тем, как смотреть в дочерний объект.

Метод loadClass в ClassLoader выполняет эти задачи, в порядке, при вызове загрузить класс:

  1. Если уже загружен класс, он возвращает его.
  2. В противном случае он делегирует поиск нового класса загрузчику родительского класса.
  3. Если загрузчик родительского класса не находит класс, loadClass вызывает метод findClass для поиска и загрузки класса.

(Understanding Extension Class Loading - Oracle)

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

  • Более простой вариант - убедиться, что загрузчик родительского класса не может найти исходный класс B.
+0

Класс «A» не загружен, так что дочерний элемент ClassLoader делегирует загрузку класса родительскому классу ClassLoader. Но B загружается в дочерний элемент (модифицированная версия загружается в дочерний элемент), поэтому, когда A требует B, потому что A расширяет B, почему B загружается из родителя (который загружает оригинал)? –

+0

Что значит «Но B загружается в ребенка»? Если B существует в загрузчике родительского класса, то он не загружается в дочерний элемент, потому что каждый загрузчик классов, который не переопределяет первых делегатов loadClass, его родительскому элементу и будет пытаться загрузить его только в дочернем классе classlaoder, если он не найден в родительский –

+0

Мне нужно изменить байт-код класса B во время выполнения. Итак, чтобы сохранить измененный класс B, я должен использовать child.defineClass(), потому что parent.defineClass() не отображается, а исходный класс B может существовать в родительском загрузчике классов. - B изменяются и загружается ребенок - А загружается из ребенка -> делегат родителей - А продолжается B - B, кажется, быть загружено из родительского - B не сохраняется на родитель, поэтому родитель нагрузка B с использованием URL-адреса Я думаю, - родительская нагрузка оригинала класса B - B загружается дважды: оригинал в родительском и изменен в дочернем. –

1

Есть несколько вещей, чтобы знать:

  • Для большинства вещей, занимающейся контекстом загрузчика класса Нити является устаревшим, так как она не имеет никакого влияния. Это больше похоже на конвенцию; установка его оказывает влияние, если есть некоторые другие запросы кода и их использование. Для процесса загрузки стандартного класса он не имеет никакого значения. К сожалению, документация не упоминает об этом и делает его похожим на релевантную вещь. Возможно, он был предназначен иметь больше смысла, когда он был добавлен.
  • В качестве pointed out by Erwin Bolwidt при загрузке A через ваш пользовательский загрузчик он делегирует его родительскому загрузчику, возвращая класс A, загруженный родителем.
  • Когда разрешает ссылки на класс, JVM всегда будет использовать , определяющий загрузчик реферера. Поэтому, когда ссылка из A на B решена, виртуальная машина будет всегда использовать родительский загрузчик, который определен класс A

Последний пункт означает, что даже если вы измените свой пользовательский загрузчик классов, чтобы искать свои собственные классы первой вместо того, чтобы сначала следовать стандартной модели запроса родителя, она не решает проблему, если у нее нет собственного A, так как тогда она все равно возвращает родительский A, ссылки на которые будут разрешены с использованием родителя. Так как вы призываете defineClass, прежде чем просить A, порядок поиска не имеет значения, так как пользовательский загрузчик имеет уже определенный B, что он возвращается, если кто-нибудь спросил его B ...

Так что вы можете позволить своему обычаю погрузчик также загружает и определяет A. Или вы используете Reflection с переопределением доступа к defineClass в системе ClassLoader перед этим загружает B. Самое чистое решение - реализовать логику модификации класса в качестве Java-агента, который может использовать API-интерфейс Instrumentation для перехвата и изменения определения B сразу после его загрузки.