2015-05-14 4 views
1

Я использую java, чтобы сделать что-то такое же, как использование динамической библиотеки C++.Использовать объект, созданный Classloader без вызова интерфейса или вызвать вызов?

Я не нашел способ напрямую использовать один и тот же объект класса без отражения кода стиля вызова.

это мой динамический библиотечный код, я делаю его банкой.

package com.demo; 

public class Logic { 
    public String doWork() { 
     System.out.println("Hello from Dll"); 
     return "Dll"; 
    } 
} 

В моем главном приложении, я могу создать экземпляр по URLClassLoader, призовите на отражения в порядке:

public class Main { 
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { 
     File file = new File("C:\\plugin.jar"); 
     URL url = file.toURI().toURL(); 
     URL[] urls = {url}; 

     ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); 
     ClassLoader loader = new URLClassLoader(urls, parentLoader); 
     Thread.currentThread().setContextClassLoader(loader); 

     Class<?> clazz = loader.loadClass("com.demo.Logic"); 

     System.out.println("New Instance!!"); 
     Object logic = clazz.newInstance(); 
     Method method = logic.getClass().getMethod("doWork"); 
     method.invoke(logic); 
} 

Выход:

New Instance!! 
Hello from Dll 

Но когда я изменить код без использования Отразить вызов:

public class Main { 
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { 
     File file = new File("C:\\plugin.jar"); 
     URL url = file.toURI().toURL(); 
     URL[] urls = {url}; 

     ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); 
     ClassLoader loader = new URLClassLoader(urls, parentLoader); 
     Thread.currentThread().setContextClassLoader(loader); 

     Class<?> clazz = loader.loadClass("com.demo.Logic"); 

     Logic logic = (Logic)clazz.newInstance(); 
     logic.doWork(); 
    } 
} 

Компиляция успеха (компилировать с внешними модулями), но когда я запускаю программу, она не на линии Logic logic = (Logic)clazz.newInstance();

Исключение:

Exception in thread "main" java.lang.NoClassDefFoundError: com/demo/Logic 
    at Main.main(Main.java:31) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) 
Caused by: java.lang.ClassNotFoundException: com.demo.Logic 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358) 
    ... 6 more 

Есть ли способ, чтобы заставить его работать? Без отражения/интерфейс. (В C++ можно легко достичь этого, доля же структура/класс DECLARE, убедитесь, используя тот же компилятор компилировать из двух частей. ИМХО Java бы сделать это также)


дополнительное объяснение 1

Я хочу, чтобы изменить текущее поведение загрузчика классов сделать это признать динамичный загруженный класс, это попытка является простым и наивным, не может найти другое направление:

 ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); 
     ClassLoader loader = new URLClassLoader(urls, parentLoader); 
     Thread.currentThread().setContextClassLoader(loader); 
+0

добро пожаловать в мир классных загрузчиков ... вы можете проверить, что «Logic.class! = Clazz» - они загружаются разными загрузчиками классов, поэтому они представляют собой разные классы (даже если они имеют то же имя класса) – ZhongYu

+0

Соответствующий - http://stackoverflow.com/questions/30227510/sandbox-for-memory/30227614#30227614 – ZhongYu

+0

@ bayou.io Да, они разные загрузчики классов, я хочу, чтобы начальный класс загрузчика распознал класс динамически – Gohan

ответ

1

я тестировал код с небольшим изменением (используется загрузчик классов по умолчанию и поместил логику в мой путь к классу). Это работает:

public class Main { 
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { 

     ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); 

     Class<?> clazz = parentLoader.loadClass("com.demo.Logic"); 

     Logic logic = (Logic)clazz.newInstance(); 
     logic.doWork(); 
    } 
} 

Ваша проблема заключается в извлечении класс из plugin.jar.


Обновлен:

Я также попытался извлечением класса из кувшина с кодом из второго примера. Я положил скомпилированный Logic.class в com \ demo и построил банку с jar cvf plugin.jar .\com\demo\Logic.class и поставил тот же путь, что и вы. Он работал без проблем.

Кроме того, вам не нужно устанавливать загрузчик классов потока потока в загрузчик. По крайней мере, не для целей данного примера.

Ваш plugin.jar может фактически не содержать класс.


Обновление:

дополнительных объяснений 1

Я хочу, чтобы изменить текущее поведение загрузчика классов, чтобы сделать его распознать динамический загруженный класс, это попытка является простым и наивным, может» т найти другое направление:

ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); 
ClassLoader loader = new URLClassLoader(urls, parentLoader); 
Thread.currentThread().setContextClassLoader(loader); 

Вы делаете это правильно с помощью загрузчика классов.

Но если вы хотите, чтобы «изменить текущее поведение загрузчика классов» вы должны прочитать this answer

загрузчиков классов предназначены для неизменны; вы не сможете willy-nilly добавлять к нему классы во время выполнения.

Таким образом, решение вы придумали с загрузчиком классов.

Вот почему я сказал: «Вам не нужно устанавливать загрузчик класса потока тока на загрузчик (дочерний загрузчик классов)».

+0

Плагин.jar создан бандой артефактов IDEA. 'Класс clazz = loader.loadClass (" com.demo.Logic ");' Работал так, что класс находится в банке. Я попробую jar класс вручную – Gohan

+0

Я нахожу, что ваше решение не использовало динамический контейнер нагрузки в классе Main. Ваш ClassLoader уже распознал класс, предварительно задав путь к классам – Gohan

+0

@Gohan Я обновил ответ на вашу дополнительную информацию. –

2

Для того, чтобы сделать эту работу, вы должны логически разделить классы на три множества:

  1. Ваш главный класс
  2. Ваш плагин классов
  3. Ваш плагин-зависимых классов

При создании нового загрузчика классов вы должны убедиться, что классы # 2 и # 3 загружены одним и тем же загрузчиком классов, потому что URLClassLoade r делегируется только родителям. Это означает, что классы в загрузчике классов приложений JVM, которые загружали ваш основной класс, не могут видеть класс в вашем новом загрузчике классов. Чтобы работать как C, вам необходимо обновить путь к классу вашего основного класса, и это не поддерживается (возможно, но не поддерживается ; я прочитал, что Java 9 удалит эту возможность).

На практике вы должны разделить свой основной класс на две части (# 1 и # 3), а затем использовать отражение, чтобы загрузить/вызвать класс, зависящий от плагина (один вызов отражения, и если ваш плагин-зависимый класс реализует Runnable, вы можете использовать ((Runnable)loadClass("PluginDependent").newInstance()).run(), чтобы уменьшить это). Тем не менее, вы должны гарантировать, что Ваш URLClassLoader не делегирует загрузку плагина в зависимости от класса, к примеру:

  1. Разделить приложение на три дискретные множества, перечисленных выше (main.jar, plugin.jar и main-plugin-dependent.jar) и перечислить все их на URLClassLoader.

  2. Изменить создание URLClassLoader указать явный нуль родителя, так что он не будет делегировать загрузчика классов приложений JVM, а затем указать как plugin.jar и ваш главный JAR.

  3. Напишите собственный URLClassLoader, который переопределяет loadClass, чтобы гарантировать, что ваши зависящие от плагинов классы загружаются загрузчиком этого класса, а не делегируются загрузчику класса приложений JVM.

+0

Я думаю, что я могу понять ваше решение, сделать «прямой код вызова» и «реализация кода» загруженным одним и тем же ClassLoader, вызвать один раз, чтобы сделать все другие вещи «прямого вызова». Я вряд ли хочу, чтобы код прямого вызова в основном проекте был таким же, как и обычный Java-код, похоже, что для каждого «прямого кода вызова» требуется несколько изменений в главном проекте. – Gohan

+0

Если вам нужно позвонить через несколько раз, затем создайте интерфейс в своем «основном» коде и реализуйте его в своем «зависящем от плагина классе», который вызывает код в классе плагинов. то есть это то же самое, что и предложение Runnable, которое я дал, за исключением того, что вы управляете интерфейсом, поэтому вы можете добавить столько вызовов перекрестного класса, сколько необходимо. –

Смежные вопросы