Может ли класс Java добавлять метод вовремя?
Может ли класс добавить метод к себе во время выполнения (например, из блока static
), так что если кто-то выполняет отражение этого класса, он увидит новый метод, хотя он не был определен в время компиляции?
Фон:
Структура, которую я использую, предполагает, что классы Action
должны быть определены, которые имеют метод doAction(...)
, по соглашению. Структура проверяет эти классы во время выполнения, чтобы узнать, какие типы параметров доступны в их методе doAction()
. Например: doAction ( String a, Integer b)
Я хотел бы, чтобы каждый класс мог программно генерировать свой метод doAction()
с различными параметрами точно в срок, когда он проверяется. Тело метода может быть пустым.
Ответы
Ответ 1
Это не просто. Когда класс загружается загрузчиком классов, нет способа изменить методы загруженных классов. Когда класс запрашивается, загрузчик классов загрузит его и свяжет. И нет способа (с Java) изменить связанный код или добавить/удалить методы.
Единственный трюк, который приходит мне на ум, - это игра с загрузчиками классов. Если мы удалим пользовательский загрузчик классов, классы, загруженные этим загрузчиком классов, должны быть удалены или недоступны. Идея, которая приходит мне на ум, заключается в
- реализовать один пользовательский загрузчик классов
- загрузите динамический класс с помощью этого пользовательского загрузчика классов
- если у нас есть обновленная версия этого класса,
- удалить пользовательский загрузчик классов и
- загрузите новую версию этого класса с помощью нового экземпляра пользовательского загрузчика классов
Я оставляю это как пищу для размышлений, не могу доказать, если это приведет к решению или у нас есть подводные камни.
Как простой ответ на вопрос: Нет, мы не можем изменить загруженный класс, так как мы можем изменить содержимое полей с отражением. (мы не можем добавлять или удалять поля тоже).
Ответ 2
Andres_D прав, мы можем очень хорошо это сделать, используя пользовательскую загрузку классов, вот подробное руководство о том, как это сделать: http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1
В статье объясняется, как писать динамический код Java. В нем обсуждается компиляция исходного кода выполнения, перезагрузка классов и использование шаблона проектирования прокси, чтобы внести изменения в динамический класс, прозрачный для его вызывающего.
Фактически исследователь в Австрии написал JVM, который даже позволяет перезагружать классы с разными иерархиями типов. Они достигли этого, используя существующие точки сохранения потока, чтобы генерировать полную "боковую вселенную" объекта и все связанные с ним ссылки и ссылочный контент, а затем после полного перетаскивания со всеми необходимыми изменениями просто заменяют все измененные классы. [1] Здесь ссылка на их проект http://ssw.jku.at/dcevm/ спонсорство оракула, безусловно, вызывает интересные размышления о будущих планах.
Менее интрузивные изменения в телах и полях методов уже возможны в стандартной виртуальной Java-среде с использованием возможностей "горячей замены" JPDA, как это представлено в Java 1.4:
docs.oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap
Я не уверен, был ли он первым, но этот документ сотрудника Sun от 2001 года, по-видимому, является одним из ранних предложений, в которых упоминаются возможности HotSpot для "горячей замены". [2]
ССЫЛКА
[1] T. Würthinger, C. Wimmer и L. Stadler, "Динамическая эволюция кода для Java", представленная на 8-й Международной конференции по принципам и практике программирования на Java, Вена, 2010 г.
[2] М. Дмитриев, "На пути к гибким и безопасным технологиям для разработки приложений Java-приложений во время выполнения", в семинаре OOPSLA по инженерно-комплексным объектно-ориентированным системам для эволюции, 2001 г.
Ответ 3
Я никогда не пробовал ничего подобного, но вы должны взглянуть на ASM, cglib и Javassist.
Ответ 4
Нет, это невозможно (легко) в Java.
Похоже, вы пытаетесь использовать Java, как если бы это был язык динамического программирования. Например, у Ruby есть открытые классы: вы можете добавлять и удалять методы из классов Ruby во время выполнения. В Ruby вы также можете использовать метод "method missing" в своем классе, который вызывается при попытке вызвать метод, который не существует в классе. Такая вещь также не существует на Java.
Существует версия Ruby, которая работает на JVM, JRuby, и ей приходится делать очень сложные трюки, чтобы открывать классы в JVM.
Ответ 5
У вас может быть метод doAction
, который делает все, что вы хотите, чтобы сгенерированный метод выполнял. Есть ли причина, по которой он должен быть сгенерирован или может быть динамическим?
Ответ 6
Я считаю, что вам нужен какой-то байт-код, изменяющий инструмент/фреймворк, такой как asm, cglib или javassist.
Вы можете достичь этого через аспекты/плетение, как это сделано Spring, но я считаю, что вам все еще нужно сначала определить метод.
Ответ 7
Прокси может помочь. Но нужно создавать экземпляр прокси каждый раз, когда вы хотите добавить или удалить метод.
Ответ 8
Я не уверен, что это возможно. Однако вы можете использовать AspectJ, ASM и т.д. И переплетать эти методы в соответствующие классы.
Другой альтернативой является использование композиции для обертывания целевого класса и предоставления метода doAction. В этом случае вы должны делегировать целевой класс.
Ответ 9
Похоже, что невозможно динамически добавлять метод. Но вы можете подготовить класс со списком методов или хешей вроде:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
public class GenericClass {
private HashMap<String, Method> methodMap = new HashMap<String, Method>();
public Object call(String methodName,Object ...args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Method method = methodMap.get(methodName);
return method.invoke(null, args);
}
public void add(String name,Method method){
if(Modifier.isStatic(method.getModifiers()))
methodMap.put(name, method);
}
public static void main(String[] args) {
try {
GenericClass task = new GenericClass();
task.add("Name",Object.class.getMethod("Name", new Class<?>[0]));
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
}
Чем, используя отражения, вы можете установить или отключить атрибут.