Вопрос по java, jar, runtime, classloader – Как получить доступ к методу из внешнего JAR во время выполнения?

6

Это продолжение вопроса, размещенного в:Как загрузить файл JAR во время выполнения

Я не уверен относительно того, как перейти к уровню вызова метода. Из моего понимания, из объекта clazz я бы использовал getMethod или getDeclaredMethod, чтобы получить объект метода, из которого я бы вызвал invoke. Конечно, для вызова требуется экземпляр. Тогда это будет то, что называется doRun в примере кода?

Нужно ли выполнять вызов метода doRun.run (), даже если я хочу выполнить метод, отличный от main (при условии, что он является main для объекта doRun, который вызывается с помощью вызова run)?

Просто для большей ясности исходного поста я спрашиваю: запускает ли doRun.run () новый поток, выполняющий экземпляр объекта класса типа clazz?

Спасибо за помощь, чтобы прояснить это для меня.

Я посмотрел накак-надо-я-нагрузка-баночка-динамически на-выполнение» (извините, разрешена только одна гиперссылка), однако это выглядело как нарушение указания зла Class.newInstance в первом посте, на который я ссылался.

Ваш Ответ

3   ответа
1

Принтер проекта:

public class Printer {

    public void display(String printtext)
    {
        System.out.println(printtext);
    }

}

Этот проект экспортируется как Printer.jar.

Класс принтера имеет методdisplay() который принимает строку в качестве входных данных.

Код вызова:

       URL url = new URL("file:Printer.jar"); 
       URLClassLoader loader = new URLClassLoader (new URL[] {url});
       Class<!--?--> cl = Class.forName ("Printer", true, loader);
       String printString = "Print this";
       Method printit = cl.getMethod("display", String.class);
       Constructor<!--?--> ctor = cl.getConstructor(); //One has to pass arguments if constructor takes input arguments.
       Object instance = ctor.newInstance();
       printit.invoke(instance, printString);
       loader.close ();

Выход:Print this

2

Пример кода

ClassLoader loader = URLClassLoader.newInstance(
    new URL[] { yourURL },
    getClass().getClassLoader()
);
Class<!--?--> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<!--? extends Runnable--> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<!--? extends Runnable--> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();

предполагает, что загружаемый вами класс реализует определенный интерфейс Runnable, и поэтому он 'Причины для приведения к этому типу с помощью asSubclass () и вызова run ().

Что вы знаете о загружаемых классах? Можете ли вы предположить, что они реализуют конкретный интерфейс? Если это так, настройте строку asSubClass (), чтобы она ссылалась на взаимодействие, которое вы предпочитаете

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

В примере нет запуска потока. Создание нового потока просто потребовало бы на пару строк больше кода

Thread myThread = new Thread(doRun);
myThread.start();
джня - спасибо за ответ. К сожалению, я не могу предположить, что знаю что-либо о классе (ах), которые я могу загружать. У меня есть один пример, который может не свидетельствовать об использовании в реальном мире. Как оно есть, я неt подкласс, получите экземпляр от конструктора, затем перейдите к использованию getDeclaredMethod, аналогично посту ниже. Спасибо за другие разъяснения, я был смущен вызовом запуска, думал, что это означает, что должен был начаться новый поток - мой плохой. Todd
3

public class ReflectionDemo {

  public void print(String str, int value) {
    System.out.println(str);
    System.out.println(value);
  }

  public static int getNumber() { return 42; }

  public static void main(String[] args) throws Exception {
    Class<!--?--> clazz = ReflectionDemo.class;
    // static call
    Method getNumber = clazz.getMethod("getNumber");
    int i = (Integer) getNumber.invoke(null /* static */);
    // instance call
    Constructor<!--?--> ctor = clazz.getConstructor();
    Object instance = ctor.newInstance();
    Method print = clazz.getMethod("print", String.class, Integer.TYPE);
    print.invoke(instance, "Hello, World!", i);
  }
}

Запись отраженных классов в интерфейс, известный по коду потребителя (как в примере), как правило, лучше, потому что позволяет избежать размышлений и использовать преимущества системы типов Java. Отражение следует использовать только тогда, когда у вас нет выбора.

Этот последний пост слишком грязный, чтобы читать, я создам новый пост. Todd
Конечно, это предполагает, что исходный код был скомпилирован с использованием интерфейса, а не того, который я создаю позже и пытаюсь привести экземпляр к. Todd
@McDowell - я потерпел неудачу в моем варианте вашего кода. Я добавил следующий метод: public void print2 (String [] strings) {for (final String string: strings) {System.out.println (string); }} со следующим в методе main: Метод print2 = clazz.getDeclaredMethod ("Print2", новый Class [] {String []. class}); print2.invoke (экземпляр, новая строка [] {"test1 ","test2"}); Это приводит к: Исключение в теме "главный" java.lang.IllegalArgumentException: неверное количество аргументов У вас есть идеи? Todd
@ Тодд - да, тыу меня это есть. Интерфейс (или некоторая другая реализация со строгим типом) часто используется с плагинами, где код был написан для динамического создания экземпляра. Если вы делаете интроспекцию и вызов произвольных классов, это не вариант. McDowell
Так что, если я понимаю ваш комментарий после кода с интерфейсом, я знаю, какие методы доступны, и могу написать код, вызывающий метод непосредственно после соответствующего приведения объекта экземпляра. Это правда? Todd

Похожие вопросы