Подтвердить что ты не робот

Может ли класс Java добавлять метод вовремя?

Может ли класс добавить метод к себе во время выполнения (например, из блока static), так что если кто-то выполняет отражение этого класса, он увидит новый метод, хотя он не был определен в время компиляции?

Фон:

Структура, которую я использую, предполагает, что классы Action должны быть определены, которые имеют метод doAction(...), по соглашению. Структура проверяет эти классы во время выполнения, чтобы узнать, какие типы параметров доступны в их методе doAction(). Например: doAction ( String a, Integer b)

Я хотел бы, чтобы каждый класс мог программно генерировать свой метод doAction() с различными параметрами точно в срок, когда он проверяется. Тело метода может быть пустым.

4b9b3361

Ответ 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();
        }
   }
}

Чем, используя отражения, вы можете установить или отключить атрибут.