#java #reflection
#java #отражение
Вопрос:
У меня большой набор данных. Я создаю систему, которая позволяет пользователям отправлять исходные файлы Java, которые затем будут применены к набору данных. Чтобы быть более конкретным, каждый отправленный исходный файл java должен содержать статический метод с определенным именем, скажем, toBeInvoked() . toBeInvoked примет строку набора данных в качестве параметра массива. Я хочу вызвать метод toBeInvoked для каждого отправленного исходного файла в каждой строке набора данных. Мне также необходимо реализовать меры безопасности (поэтому toBeInvoked() не может выполнять ввод-вывод, не может вызывать exit и т. Д.).
В настоящее время моя реализация такова: у меня есть список имен исходных файлов Java. Для каждого файла я создаю экземпляр пользовательского безопасного загрузчика классов, который я закодировал, который компилирует исходный файл и возвращает скомпилированный класс. Я использую отражение для извлечения статического метода toBeInvoked() (например, method = c.GetMethod(«toBeInvoked», double[].class) ). Затем я перебираю строки набора данных и вызываю метод в каждой строке.
В моем подходе есть как минимум две проблемы:
- кажется, это мучительно медленно (я слышал, что отражение имеет тенденцию быть медленным)
- код сложнее, чем хотелось бы
Есть ли лучший способ выполнить то, что я пытаюсь сделать?
Комментарии:
1. Дорогая вещь здесь, скорее всего, не рефлексивный вызов (если он действительно не вызывается очень часто), а компиляция! Как вы это компилируете? Используя встроенный
JavaCompiler
класс или через какой-либо внешнийRuntime.exec
вызов? ` В любом случае, помимо компиляции, функциональность «плагинов» обычно может быть достигнута с помощью docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html , хотя не совсем ясно, применимо ли это в вашем случае.
Ответ №1:
Нет значительно лучшего подхода, учитывая ограничения, которые вы установили сами.
Для чего это стоит, что делает это «мучительно медленным», так это компиляция исходных файлов в файлы классов и их загрузка. Это на много порядков медленнее, чем использование отражения для вызова методов.
(Использование общего интерфейса вместо статических методов не приведет к ощутимой разнице в скорости, а снижение сложности относительно невелико.)
Если вы действительно хотите упростить и ускорить это, измените свою архитектуру так, чтобы код предоставлялся в виде файла JAR, содержащего все скомпилированные классы.
Ответ №2:
Предполагая, что ваш #toBeInvoked() может быть определен в интерфейсе, а не быть статическим (так и должно быть!), Вы можете просто загрузить класс и привести его к интерфейсу:
Class<? extends YourInterface> c = Class.forName("name", true, classLoader).asSubclass(YourInterface.class);
YourInterface i = c.newInstance();
После этого вызовите #toBeInvoked() напрямую.
Также загляните в java.util .ServiceLoader, который может быть полезен для поиска правильного класса для загрузки в случае, если у вас более одного исходного файла.
Ответ №3:
Лично я бы использовал интерфейс. Это позволит вам иметь несколько экземпляров со своим собственным состоянием (полезно для многопоточности), но, что более важно, вы можете использовать интерфейс, сначала для определения того, какие методы должны быть реализованы, а также для вызова методов.
Отражение происходит медленно, но это только относительно других параметров, таких как прямой вызов метода. Если вы сканируете большой набор данных, тот факт, что вам приходится извлекать данные из основной памяти, вероятно, будет намного дороже.
Ответ №4:
Я бы предложил следующие шаги для вашей проблемы.
- Чтобы проверить, содержит ли метод какой-либо нежелательный код, вам необходимо иметь сценарий проверки, который может выполнять эти проверки во время загрузки.
- Создайте интерфейс, содержащий метод toBeInvoked() (не статический метод).
- Все загружаемые классы должны реализовывать этот интерфейс и добавлять логику внутри этого метода.
- вы можете заставить свой пользовательский загрузчик классов сканировать определенную папку на предмет добавления новых классов и загружать их соответствующим образом.
- Когда файл загружен и успешно проверен, вы можете скомпилировать и скопировать файл класса в папку, которую сканирует загрузчик классов.
- Ваш класс процессора может искать новые файлы, а затем вызывать метод toBeInvoked() для загруженного класса, когда это необходимо.
Надеюсь, это поможет. (Обратите внимание, что я использовал аналогичный механизм для динамической загрузки классов шагов рабочего процесса в инструменте Workflow Engine, который был разработан).