Как загрузить файл JAR во время выполнения

Меня попросили построить систему Java, которая будет иметь возможность загружать новый код (расширения) во время работы. Как я могу повторно загрузить файл JAR, когда мой код работает? или как мне загрузить новую банку?

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

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

5 ответов

Решение

Перезагрузка существующих классов с существующими данными может привести к поломке.

Вы можете относительно легко загрузить новый код в загрузчики новых классов:

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

Загрузчики классов, которые больше не используются, могут быть собраны сборщиком мусора (если нет утечки памяти, как это часто бывает при использовании ThreadLocal, драйверов JDBC, java.beans, так далее).

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

Конечно, системы отладки могут делать более причудливые вещи, но они более хакерские и менее надежные.

Можно добавить новые классы в загрузчик классов. Например, используя URLClassLoader.addURL, Однако, если класс не загружается (потому что, скажем, вы его не добавили), он никогда не загрузится в этом экземпляре загрузчика классов.

Это работает для меня:

File file  = new File("c:\\myjar.jar");

URL url = file.toURL();  
URL[] urls = new URL[]{url};

ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("com.mypackage.myclass");

Меня попросили построить систему Java, которая будет иметь возможность загружать новый код во время работы

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

Неразбериха с загрузчиками классов - дело очень сложное, в основном из-за того, как работает видимость классов, и вы не захотите позже столкнуться с трудными для отладки проблемами. Например, Class.forName (), который широко используется во многих библиотеках, не слишком хорошо работает на фрагментированном пространстве загрузчика классов.

Я немного погуглил и нашел этот код здесь:

File file = getJarFileToLoadFrom();   
String lcStr = getNameOfClassToLoad();   
URL jarfile = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");    
URLClassLoader cl = URLClassLoader.newInstance(new URL[] {jarfile });   
Class loadedClass = cl.loadClass(lcStr);   

Кто-нибудь может поделиться мнениями / комментариями / ответами относительно этого подхода?

Используйте org.openide.util.Lookup и ClassLoader для динамической загрузки плагина Jar, как показано здесь.

public LoadEngine() {
    Lookup ocrengineLookup;
    Collection<OCREngine> ocrengines;
    Template ocrengineTemplate;
    Result ocrengineResults;
    try {
        //ocrengineLookup = Lookup.getDefault(); this only load OCREngine in classpath of  application
        ocrengineLookup = Lookups.metaInfServices(getClassLoaderForExtraModule());//this load the OCREngine in the extra module as well
        ocrengineTemplate = new Template(OCREngine.class);
        ocrengineResults = ocrengineLookup.lookup(ocrengineTemplate); 
        ocrengines = ocrengineResults.allInstances();//all OCREngines must implement the defined interface in OCREngine. Reference to guideline of implement org.openide.util.Lookup for more information

    } catch (Exception ex) {
    }
}

public ClassLoader getClassLoaderForExtraModule() throws IOException {

    List<URL> urls = new ArrayList<URL>(5);
    //foreach( filepath: external file *.JAR) with each external file *.JAR, do as follows
    File jar = new File(filepath);
    JarFile jf = new JarFile(jar);
    urls.add(jar.toURI().toURL());
    Manifest mf = jf.getManifest(); // If the jar has a class-path in it's manifest add it's entries
    if (mf
            != null) {
        String cp =
                mf.getMainAttributes().getValue("class-path");
        if (cp
                != null) {
            for (String cpe : cp.split("\\s+")) {
                File lib =
                        new File(jar.getParentFile(), cpe);
                urls.add(lib.toURI().toURL());
            }
        }
    }
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    if (urls.size() > 0) {
        cl = new URLClassLoader(urls.toArray(new URL[urls.size()]), ClassLoader.getSystemClassLoader());
    }
    return cl;
}
Другие вопросы по тегам