Может ли класс Java добавить метод к себе во время выполнения? - PullRequest
56 голосов
/ 13 июля 2011

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

Справочная информация:

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

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

Ответы [ 10 ]

53 голосов
/ 13 июля 2011

Это не просто.Как только класс загружается загрузчиком классов, невозможно изменить методы загруженных классов.Когда запрашивается класс, загрузчик классов загрузит его и ссылка .И нет никакого способа (с Java) изменить связанный код или добавить / удалить методы.

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

  1. реализовать один пользовательский загрузчик классов
  2. загрузить класс dynamic с этим пользовательским загрузчиком классов
  3. , если мыиметь обновленную версию этого класса,
  4. удалить пользовательский загрузчик классов и
  5. загрузить новую версию этого класса с новым экземпляром пользовательского загрузчика классов

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

В качестве простого ответа на вопрос: нет, мы не можем изменить загруженный класс, как мы можем изменить содержимое полей с помощью отражения.(мы не можем добавлять или удалять поля).

22 голосов
/ 15 сентября 2012

Andres_D прав, мы можем очень хорошо сделать это, используя пользовательскую загрузку классов, вот подробное руководство о том, как это сделать: http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1

В статье объясняется, как писать динамический код Java. В нем рассматриваются компиляция исходного кода во время выполнения, перезагрузка класса и использование шаблона проектирования Proxy для внесения изменений в динамический класс, прозрачный для его вызывающей стороны.

Фактически, исследователь в Австрии написал 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 для Hot Swap. [2]

ССЫЛКА

[1] Т. Вюртингер, К. Виммер и Л. Стадлер, «Динамическая эволюция кода для Java», представленная на 8-й Международной конференции по принципам и практике программирования на Java, Вена, 2010.

[2] М. Дмитриев, «На пути к гибкой и безопасной технологии для эволюции приложений на языке Java во время выполнения», на семинаре OOPSLA по проектированию сложных объектно-ориентированных систем для эволюции, 2001 г.

8 голосов
/ 13 июля 2011

Я никогда не пробовал ничего подобного, но вам стоит взглянуть на ASM , cglib и Javassist .

4 голосов
/ 13 июля 2011

Нет, это (легко) невозможно в Java.

Похоже, вы пытаетесь использовать Java, как если бы это был динамический язык программирования.Например, в Ruby есть открытые классы: вы можете добавлять и удалять методы из классов Ruby во время выполнения.В Ruby у вас также может быть метод «отсутствует метод» в вашем классе, который будет вызываться при попытке вызвать метод, которого нет в классе.Этого также не существует в Java.

Существует версия Ruby, которая работает на JVM, JRuby, и она должна делать очень сложные трюки, чтобы заставить открытые классы работать на JVM.

2 голосов
/ 13 июля 2011

Вы можете иметь метод doAction, который делает все, что вы хотите, чтобы сгенерированный метод делал.Есть ли причина, по которой его нужно сгенерировать или он может быть динамическим?

1 голос
/ 30 апреля 2018

То, что я предлагаю, должно работать в вашей ситуации: 1. У вас есть существующий класс MyClass с n методами. 2. Вы хотите включить (n + 1) -й метод, которого нет в классе, при компиляции в другом исходном файле .java.

Мой способ решить это - Наследование.Создайте новый исходный файл .java для класса MyClassPlusOne, расширяющего первый класс MyClass.Скомпилируйте этот класс и используйте объект. Как мне скомпилировать и развернуть Java-класс во время выполнения?

class MyClassPlusOne extends MyClass
{
     void doAction(String a, Integer b)
     {
         int myNPlus1 = a+b;
         //add whatever you want before compiling this code
      }
}
1 голос
/ 27 декабря 2013

Прокси может помочь. Но приходится создавать экземпляр Proxy каждый раз, когда вы хотите добавить или удалить метод.

1 голос
/ 13 июля 2011

Полагаю, вам нужен какой-нибудь инструмент / инфраструктура для изменения байт-кода, например asm, cglib или javassist.Вы можете достичь этого с помощью аспектов / плетения, как это было сделано в Spring, но я считаю, что вам все еще нужно сначала определить метод.

0 голосов
/ 16 мая 2016

Похоже, что нет способа добавить метод динамически. Но вы можете подготовить класс со списком методов или хешем, например:

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();
        }
   }
}

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

0 голосов
/ 13 июля 2011

Я не уверен, что это возможно.Однако вы можете использовать AspectJ, ASM и т. Д. И объединить эти методы в соответствующие классы.

Другой альтернативой является использование композиции для переноса целевого класса и предоставления метода doAction.В этом случае вы должны делегировать целевому классу.

...